queue = new LinkedList<>();
+ queue.offer(this.root);
+
+ while (!queue.isEmpty()) {
+ SnapshotLeaf currentLeaf = queue.poll();
+ result.add(currentLeaf.getInventory());
+ for (SnapshotLeaf child : currentLeaf.getChildren()) {
+ queue.offer(child);
+ }
+ }
+
+ return result;
+ }
}
diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotVO_.java b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotVO_.java
index 2d5abaf4f7f..8aeb5873f6b 100755
--- a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotVO_.java
+++ b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotVO_.java
@@ -1,9 +1,7 @@
package org.zstack.header.storage.snapshot;
-/**
- */
-
import javax.persistence.metamodel.StaticMetamodel;
+import java.sql.Timestamp;
@StaticMetamodel(VolumeSnapshotVO.class)
public class VolumeSnapshotVO_ extends VolumeSnapshotAO_ {
diff --git a/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceEvent.java b/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceEvent.java
old mode 100755
new mode 100644
diff --git a/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceEventDoc_zh_cn.groovy
old mode 100755
new mode 100644
diff --git a/header/src/main/java/org/zstack/header/vm/MetadataImpact.java b/header/src/main/java/org/zstack/header/vm/MetadataImpact.java
new file mode 100644
index 00000000000..8c982b7687f
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/MetadataImpact.java
@@ -0,0 +1,71 @@
+package org.zstack.header.vm;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标注 API 消息对虚拟机元数据的影响类型。
+ *
+ * opt-out 策略
+ * 不标注时默认行为等同于 {@link Impact#CONFIG}。
+ * 明确不影响元数据的 API 应标注 {@link Impact#NONE}。
+ *
+ * vmUuid 解析
+ * 不涉及 VM 的 API(如 APICreateZoneMsg)即使默认 CONFIG,
+ * 也不会触发元数据更新——因为 {@link VmUuidFromApiResolver} 无法解析出 vmUuid,
+ * 不会产生 {@link UpdateVmInstanceMetadataMsg}。
+ *
+ * @see VmUuidFromApiResolver
+ * @see UpdateVmInstanceMetadataMsg
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface MetadataImpact {
+
+ /**
+ * 影响类型。
+ */
+ Impact value();
+
+ /**
+ * API 失败时是否也需要更新元数据。
+ *
+ * 默认 false:仅在 API 成功后触发元数据更新。
+ * 设为 true 时,API 执行失败也会触发 markDirty。
+ * 适用于 API 可能部分成功、需要同步最新状态的场景。
+ */
+ boolean updateOnFailure() default false;
+
+ /**
+ * API 对虚拟机元数据的影响类型枚举。
+ */
+ enum Impact {
+ /**
+ * 不影响虚拟机元数据,明确跳过。
+ *
+ * 用于标注与 VM 无关或虽关联 VM 但不影响元数据内容的 API,
+ * 如 APIQueryVmInstanceMsg、APIGetVmConsoleAddressMsg 等。
+ */
+ NONE,
+
+ /**
+ * 影响虚拟机配置,触发元数据更新。
+ *
+ * 如修改 CPU/内存、增删 SystemTag/ResourceConfig 等。
+ * 这是未标注 {@link MetadataImpact} 注解时的默认行为。
+ */
+ CONFIG,
+
+ /**
+ * 影响存储结构,触发元数据更新。
+ *
+ * 如存储迁移、快照操作、删除云盘等涉及存储结构变更的 API。
+ * 在 sblk 场景下会设置 pending_op=2 以标记存储结构变更。
+ */
+ STORAGE
+ }
+}
\ No newline at end of file
diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java
new file mode 100644
index 00000000000..e918ad6b075
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java
@@ -0,0 +1,43 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.NeedReplyMessage;
+
+/**
+ * 更新虚拟机元数据消息(MN 内部)。
+ *
+ * 调用链第 1 步:由 API 完成后的拦截器发出,路由到 VM 所在的 MN 节点。
+ * 接收方从 DB 构建 {@link VmInstanceMetadataDTO},编码后发送
+ * {@link UpdateVmInstanceMetadataOnPrimaryStorageMsg}。
+ *
+ * @see UpdateVmInstanceMetadataOnPrimaryStorageMsg
+ * @see UpdateVmInstanceMetadataOnHypervisorMsg
+ */
+public class UpdateVmInstanceMetadataMsg extends NeedReplyMessage implements VmInstanceMessage {
+
+ private String vmInstanceUuid;
+
+ /**
+ * 是否涉及存储结构变更。
+ *
+ * 对应 {@link MetadataImpact.Impact#STORAGE} 类型的操作。
+ * sblk 场景下会设置 pending_op=2。
+ */
+ 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/UpdateVmInstanceMetadataOnHypervisorMsg.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorMsg.java
new file mode 100644
index 00000000000..61e0ebde900
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorMsg.java
@@ -0,0 +1,86 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.host.HostMessage;
+import org.zstack.header.message.NeedReplyMessage;
+
+/**
+ * 在 Hypervisor 上更新虚拟机元数据消息。
+ *
+ * 调用链第 3 步(可选):发送到 Host Agent 执行实际的存储写入。
+ *
+ * 使用场景
+ *
+ * - sblk:需要通过 Host Agent 操作 LV(activate → write → deactivate)
+ * - local:数据在本地磁盘,需要通过 Host Agent 写入
+ * - NFS:通常通过 PS Agent 直接操作,不使用此消息
+ *
+ *
+ * @see UpdateVmInstanceMetadataMsg
+ * @see UpdateVmInstanceMetadataOnPrimaryStorageMsg
+ */
+public class UpdateVmInstanceMetadataOnHypervisorMsg extends NeedReplyMessage implements HostMessage {
+
+ private String hostUuid;
+ private String vmInstanceUuid;
+
+ /**
+ * 元数据文件在存储上的路径。
+ *
+ *
+ * - sblk:LV 设备路径,如 /dev/{vg_uuid}/{vm_uuid}_vmmeta
+ * - local:本地文件路径,如 /path/to/vm/vm_metadata.json
+ *
+ */
+ private String metadataPath;
+
+ /**
+ * 元数据 JSON 字符串。
+ */
+ private String metadata;
+
+ /**
+ * 是否涉及存储结构变更(sblk 场景设置 pending_op=2)。
+ */
+ private boolean storageStructureChange;
+
+ @Override
+ public String getHostUuid() {
+ return hostUuid;
+ }
+
+ public void setHostUuid(String hostUuid) {
+ this.hostUuid = hostUuid;
+ }
+
+ public String getVmInstanceUuid() {
+ return vmInstanceUuid;
+ }
+
+ public void setVmInstanceUuid(String vmInstanceUuid) {
+ this.vmInstanceUuid = vmInstanceUuid;
+ }
+
+ public String getMetadataPath() {
+ return metadataPath;
+ }
+
+ public void setMetadataPath(String metadataPath) {
+ this.metadataPath = metadataPath;
+ }
+
+ public String getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(String metadata) {
+ this.metadata = metadata;
+ }
+
+ public boolean isStorageStructureChange() {
+ return storageStructureChange;
+ }
+
+ public void setStorageStructureChange(boolean storageStructureChange) {
+ this.storageStructureChange = storageStructureChange;
+ }
+}
diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java
new file mode 100644
index 00000000000..036403b01b2
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java
@@ -0,0 +1,9 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.MessageReply;
+
+/**
+ * {@link UpdateVmInstanceMetadataOnHypervisorMsg} 的回复。
+ */
+public class UpdateVmInstanceMetadataOnHypervisorReply extends MessageReply {
+}
diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java
new file mode 100644
index 00000000000..41ba6d9b254
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java
@@ -0,0 +1,84 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.NeedReplyMessage;
+import org.zstack.header.storage.primary.PrimaryStorageMessage;
+
+/**
+ * 在主存储上更新虚拟机元数据消息。
+ *
+ * 调用链第 2 步:发送到主存储服务,由主存储根据自身类型决定写入方式:
+ *
+ * - sblk/local:进一步发送 {@link UpdateVmInstanceMetadataOnHypervisorMsg} 到 Host Agent
+ * - NFS:直接通过 PS Agent 写入
+ *
+ *
+ * @see UpdateVmInstanceMetadataMsg
+ * @see UpdateVmInstanceMetadataOnHypervisorMsg
+ */
+public class UpdateVmInstanceMetadataOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage {
+
+ private String primaryStorageUuid;
+ private String vmInstanceUuid;
+
+ /**
+ * 根盘 UUID,用于 PS handler 定位元数据写入路径。
+ *
+ * LocalStorage 通过根盘 installPath 推导元数据文件路径;
+ * NFS 通过根盘关联的 Host 确定转发目标。
+ */
+ private String rootVolumeUuid;
+
+ /**
+ * 元数据 JSON 字符串。
+ *
+ * 由 {@code VmInstanceBase.buildVmInstanceMetadata()} 从 DB 全量构建,
+ * 为 {@link VmInstanceMetadataDTO} 的 JSON 序列化结果。
+ */
+ private String metadata;
+
+ /**
+ * 是否涉及存储结构变更(sblk 场景设置 pending_op=2)。
+ */
+ private boolean storageStructureChange;
+
+ @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 getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(String metadata) {
+ this.metadata = metadata;
+ }
+
+ public boolean isStorageStructureChange() {
+ return storageStructureChange;
+ }
+
+ public void setStorageStructureChange(boolean storageStructureChange) {
+ this.storageStructureChange = storageStructureChange;
+ }
+}
diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageReply.java
new file mode 100644
index 00000000000..475855f2b67
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnPrimaryStorageReply.java
@@ -0,0 +1,9 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.MessageReply;
+
+/**
+ * {@link UpdateVmInstanceMetadataOnPrimaryStorageMsg} 的回复。
+ */
+public class UpdateVmInstanceMetadataOnPrimaryStorageReply extends MessageReply {
+}
diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java
new file mode 100644
index 00000000000..61a2d4dbd1b
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java
@@ -0,0 +1,9 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.MessageReply;
+
+/**
+ * {@link UpdateVmInstanceMetadataMsg} 的回复。
+ */
+public class UpdateVmInstanceMetadataReply extends MessageReply {
+}
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java b/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
index 9d0efdd77f1..a2716386957 100755
--- a/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
@@ -96,4 +96,6 @@ enum Capability {
String VM_CDROM_OCCUPANT_ISO = "ISO";
String VM_CDROM_OCCUPANT_GUEST_TOOLS = "GuestTools";
+
+ String VM_META_SUFFIX = "_meta";
}
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataCodec.java b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataCodec.java
new file mode 100644
index 00000000000..2153f9247cb
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataCodec.java
@@ -0,0 +1,96 @@
+package org.zstack.header.vm;
+
+import org.zstack.utils.gson.JSONObjectUtil;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * 虚拟机元数据编解码器。
+ *
+ * 负责 {@link VmInstanceMetadataDTO} 与存储介质之间的编解码:
+ *
+ * 序列化流程:DTO → JSON String → Base64 String → byte[](写入存储)
+ * 反序列化流程:byte[](读取存储) → Base64 String → JSON String → DTO
+ *
+ *
+ * 单层 Base64 编码策略:DTO 内部所有字段为明文 JSON,
+ * 仅在写入存储时做一次 Base64 编码。
+ */
+public class VmInstanceMetadataCodec {
+
+ private VmInstanceMetadataCodec() {
+ }
+
+ /**
+ * 将 DTO 编码为可写入存储的字节数组。
+ *
+ * @param dto 元数据 DTO
+ * @return Base64 编码后的字节数组
+ */
+ public static byte[] encode(VmInstanceMetadataDTO dto) {
+ String json = JSONObjectUtil.toJsonString(dto);
+ return Base64.getEncoder().encode(json.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * 将 DTO 编码为 Base64 字符串。
+ *
+ * @param dto 元数据 DTO
+ * @return Base64 编码后的字符串
+ */
+ public static String encodeToString(VmInstanceMetadataDTO dto) {
+ String json = JSONObjectUtil.toJsonString(dto);
+ return Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * 从存储读取的字节数组解码为 DTO。
+ *
+ * @param data Base64 编码的字节数组
+ * @return 元数据 DTO
+ * @throws IllegalArgumentException 如果 Base64 解码失败或 JSON 格式错误
+ */
+ public static VmInstanceMetadataDTO decode(byte[] data) {
+ byte[] jsonBytes = Base64.getDecoder().decode(data);
+ String json = new String(jsonBytes, StandardCharsets.UTF_8);
+ return JSONObjectUtil.toObject(json, VmInstanceMetadataDTO.class);
+ }
+
+ /**
+ * 从 Base64 字符串解码为 DTO。
+ *
+ * @param base64 Base64 编码的字符串
+ * @return 元数据 DTO
+ * @throws IllegalArgumentException 如果 Base64 解码失败或 JSON 格式错误
+ */
+ public static VmInstanceMetadataDTO decodeFromString(String base64) {
+ byte[] jsonBytes = Base64.getDecoder().decode(base64);
+ String json = new String(jsonBytes, StandardCharsets.UTF_8);
+ return JSONObjectUtil.toObject(json, VmInstanceMetadataDTO.class);
+ }
+
+ /**
+ * 将 DTO 序列化为 JSON 字符串(不做 Base64 编码)。
+ *
+ * 用于调试、日志、一致性检查等场景。
+ *
+ * @param dto 元数据 DTO
+ * @return JSON 字符串
+ */
+ public static String toJson(VmInstanceMetadataDTO dto) {
+ return JSONObjectUtil.toJsonString(dto);
+ }
+
+ /**
+ * 从 JSON 字符串反序列化为 DTO(不做 Base64 解码)。
+ *
+ * 用于调试、测试等场景。
+ *
+ * @param json JSON 字符串
+ * @return 元数据 DTO
+ */
+ public static VmInstanceMetadataDTO fromJson(String json) {
+ return JSONObjectUtil.toObject(json, VmInstanceMetadataDTO.class);
+ }
+}
\ No newline at end of file
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataConstants.java b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataConstants.java
new file mode 100644
index 00000000000..ec9b5a231ee
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataConstants.java
@@ -0,0 +1,82 @@
+package org.zstack.header.vm;
+
+/**
+ * 虚拟机元数据相关常量。
+ */
+public class VmInstanceMetadataConstants {
+
+ private VmInstanceMetadataConstants() {
+ }
+
+ /**
+ * 元数据 LV 后缀(sblk 场景)。
+ *
+ * LV 命名规则:{vm_uuid}_vmmeta
+ */
+ public static final String SBLK_LV_SUFFIX = "_vmmeta";
+
+ /**
+ * 元数据文件名(local/NFS 场景)。
+ *
+ * 文件位于与根盘同目录下。
+ */
+ public static final String METADATA_FILE_NAME = "vm_metadata.json";
+
+ /**
+ * sblk 元数据 LV 默认初始大小(字节):4MB。
+ */
+ public static final long SBLK_LV_INITIAL_SIZE = 4L * 1024 * 1024;
+
+ /**
+ * sblk 元数据 LV 最大大小(字节):64MB。
+ */
+ public static final long SBLK_LV_MAX_SIZE = 64L * 1024 * 1024;
+
+ /**
+ * sblk 写入序列号最大值。溢出后回绕到 1。
+ */
+ public static final long MAX_WRITE_SEQUENCE = 0xFFFFFFFFFFFFFFFFL;
+
+ /**
+ * 全局配置:是否启用虚拟机元数据记录。
+ *
+ * 默认关闭。开启后,API 操作成功时自动触发元数据更新。
+ */
+ public static final String GLOBAL_CONFIG_METADATA_ENABLED = "vm.metadata.enabled";
+
+ /**
+ * GC 初始延迟秒数。
+ *
+ * API 成功后延迟该秒数再触发元数据更新,
+ * 避免短时间内多次 API 操作产生过多无用更新。
+ */
+ public static final int INITIAL_GC_DELAY_SECONDS = 5;
+
+ /**
+ * 注册虚拟机 MN 标识 System Tag 前缀。
+ *
+ * 注册过程中在 VM 上打标记,记录执行注册的 MN UUID,
+ * 用于 MN 崩溃后的事务回滚判断。
+ */
+ public static final String REGISTERING_MN_TAG_PREFIX = "vmMetadata::registeringMnUuid::";
+
+ /**
+ * VM 状态:注册中。
+ *
+ * 注册开始时 VM 进入此中间状态,注册完成后转为 Stopped。
+ */
+ public static final String VM_STATE_REGISTERING = "Registering";
+
+ /**
+ * ChainTask 最大排队任务数。
+ *
+ * 同一 VM 的元数据更新 ChainTask 最多排队 1 个,
+ * 超出的通过 exceedMaxPendingCallback 立即 Done。
+ */
+ public static final int MAX_PENDING_METADATA_TASKS = 1;
+
+ /**
+ * ChainTask syncSignature 前缀。
+ */
+ public static final String CHAIN_TASK_SIGNATURE_PREFIX = "vm-metadata-update-";
+}
\ No newline at end of file
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataDTO.java b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataDTO.java
new file mode 100644
index 00000000000..89afda3759b
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataDTO.java
@@ -0,0 +1,132 @@
+package org.zstack.header.vm;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 虚拟机元数据 DTO。
+ *
+ * 存储在主存储上的元数据文件内容就是该 DTO 的 JSON 字符串经 Base64 编码后的结果。
+ *
+ * 编码策略
+ * DTO 内部所有字段均为明文 JSON。由存储写入层对整个 DTO 的 JSON 字符串做一次统一
+ * Base64 编码后写入存储介质(sblk Slot Payload / local NFS 文件内容)。
+ *
+ * Checksum
+ * Checksum 不作为 DTO 字段,由存储层保证:
+ *
+ * - sblk: Slot 结构自带 Checksum 字段
+ * - local/NFS: tmp + rename 原子写入保证完整性
+ *
+ */
+public class VmInstanceMetadataDTO {
+
+ /**
+ * 资源元数据子结构。
+ *
+ * 对于每种资源(VM、Volume、Nic),记录其 VO 全量 JSON 及关联的 SystemTag/ResourceConfig。
+ */
+ public static class ResourceMetadata {
+ /**
+ * 资源 UUID。
+ *
+ * 冗余字段,反序列化时必须校验与 {@link #vo} 内部的 uuid 字段一致。
+ */
+ public String resourceUuid;
+
+ /**
+ * VO 全量 JSON 明文。
+ *
+ *
+ * - {@link VmInstanceMetadataDTO#vm} → VmInstanceVO JSON
+ * - {@link VmInstanceMetadataDTO#volumes} 元素 → VolumeVO JSON
+ * - {@link VmInstanceMetadataDTO#nics} 元素 → VmNicVO JSON
+ *
+ *
+ * 序列化时由 Gson 自动处理嵌套 JSON 的转义;反序列化时需要二次反序列化为具体 VO 类。
+ */
+ public String vo;
+
+ /**
+ * SystemTag 列表的 Base64 编码。
+ *
+ * 构建过程:SystemTagVO 列表 → 逐个 JSON 序列化 → 组成 JSON Array 字符串 → Base64 编码。
+ * Base64 编码是为了保护可能包含的密码、密钥等敏感信息。
+ */
+ public String systemTags;
+
+ /**
+ * ResourceConfig 列表的 Base64 编码。
+ *
+ * 构建过程与 systemTags 一致。
+ */
+ public String resourceConfigs;
+ }
+
+ /**
+ * 元数据 schema 版本,与 ZStack 数据库版本(zsv)一致,如 "5.0.0"。
+ *
+ * 序列化时自动填充当前平台版本。注册时若版本不匹配则拒绝注册。
+ * 升级后通过全量更新 GC 将所有 VM 的元数据刷新到新版本。
+ */
+ public String schemaVersion;
+
+ /**
+ * 虚拟机自身的元数据。
+ *
+ * {@link ResourceMetadata#vo} 为 VmInstanceVO 的 JSON。
+ */
+ public ResourceMetadata vm;
+
+ /**
+ * 云盘元数据列表。
+ *
+ * 包含根盘与数据盘(挂载的 + 已卸载但 lastVmInstanceUuid 指向本 VM 的)。
+ * {@link ResourceMetadata#vo} 为 VolumeVO 的 JSON。
+ */
+ public List volumes;
+
+ /**
+ * 网卡元数据列表。
+ *
+ * 仅记录,注册时不恢复。{@link ResourceMetadata#vo} 为 VmNicVO 的 JSON。
+ */
+ public List nics;
+
+ /**
+ * 快照数据。
+ *
+ * Key 为 volumeUuid,Value 为该 volume 下所有 VolumeSnapshotVO 的 JSON 明文列表。
+ */
+ public Map> snapshots;
+
+ /**
+ * 快照组列表。
+ *
+ * 每个元素是 VolumeSnapshotGroupVO 的 JSON 明文。
+ */
+ public List snapshotGroups;
+
+ /**
+ * 快照组关联引用列表。
+ *
+ * 每个元素是 VolumeSnapshotGroupRefVO 的 JSON 明文。
+ * 通过 {@code volumeSnapshotGroupUuid} 字段与 {@link #snapshotGroups} 关联。
+ */
+ public List snapshotGroupRefs;
+
+ /**
+ * 快照引用数据。
+ *
+ * Key 为 volumeUuid,Value 为该 volume 下所有 VolumeSnapshotReferenceVO 的 JSON 明文列表。
+ * 使用 {@code List} 而非单值,因为同一 volume 可能存在多条引用记录。
+ */
+ public Map> snapshotReferences;
+
+ /**
+ * 快照引用树数据。
+ *
+ * Key 为 volumeUuid,Value 为该 volume 下所有 VolumeSnapshotReferenceTreeVO 的 JSON 明文列表。
+ */
+ public Map> snapshotReferenceTrees;
+}
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataRegistrationSpec.java b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataRegistrationSpec.java
new file mode 100644
index 00000000000..a3ea3981759
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataRegistrationSpec.java
@@ -0,0 +1,92 @@
+package org.zstack.header.vm;
+
+/**
+ * 虚拟机元数据注册参数。
+ *
+ * 封装从元数据注册虚拟机时需要的新环境上下文信息。
+ *
+ * 字段处理矩阵中标记为"API 参数"或"替换"的字段,其新值来源于此对象。
+ */
+public class VmInstanceMetadataRegistrationSpec {
+
+ /**
+ * 注册目标 Zone UUID(必填)。
+ *
+ * 替换 VmInstanceVO.zoneUuid。
+ */
+ private String zoneUuid;
+
+ /**
+ * 注册目标主存储 UUID(必填)。
+ *
+ * 替换 VolumeVO.primaryStorageUuid、VolumeSnapshotVO.primaryStorageUuid。
+ */
+ private String primaryStorageUuid;
+
+ /**
+ * 注册操作的账户 UUID。
+ *
+ * 替换所有 VO 的 accountUuid 字段。通常为 admin。
+ */
+ private String accountUuid;
+
+ /**
+ * 旧存储路径标识符。
+ *
+ *
+ * - sblk 场景:旧 VG UUID
+ * - local/NFS 场景:旧路径前缀(如 /vms_ds)
+ *
+ */
+ private String oldPathIdentifier;
+
+ /**
+ * 新存储路径标识符。
+ *
+ *
+ * - sblk 场景:新 VG UUID
+ * - local/NFS 场景:新路径前缀(如 /vms_ds2)
+ *
+ */
+ private String newPathIdentifier;
+
+ public String getZoneUuid() {
+ return zoneUuid;
+ }
+
+ public void setZoneUuid(String zoneUuid) {
+ this.zoneUuid = zoneUuid;
+ }
+
+ public String getPrimaryStorageUuid() {
+ return primaryStorageUuid;
+ }
+
+ public void setPrimaryStorageUuid(String primaryStorageUuid) {
+ this.primaryStorageUuid = primaryStorageUuid;
+ }
+
+ public String getAccountUuid() {
+ return accountUuid;
+ }
+
+ public void setAccountUuid(String accountUuid) {
+ this.accountUuid = accountUuid;
+ }
+
+ public String getOldPathIdentifier() {
+ return oldPathIdentifier;
+ }
+
+ public void setOldPathIdentifier(String oldPathIdentifier) {
+ this.oldPathIdentifier = oldPathIdentifier;
+ }
+
+ public String getNewPathIdentifier() {
+ return newPathIdentifier;
+ }
+
+ public void setNewPathIdentifier(String newPathIdentifier) {
+ this.newPathIdentifier = newPathIdentifier;
+ }
+}
\ No newline at end of file
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataValidator.java b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataValidator.java
new file mode 100644
index 00000000000..2a9722511ad
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceMetadataValidator.java
@@ -0,0 +1,143 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.exception.CloudRuntimeException;
+import org.zstack.utils.gson.JSONObjectUtil;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 虚拟机元数据校验器。
+ *
+ * 在反序列化后、注册前执行校验,确保元数据完整性和一致性。
+ *
+ * 校验项:
+ *
+ * - schemaVersion 与当前平台版本匹配
+ * - ResourceMetadata.resourceUuid 与 vo 内部 uuid 一致
+ * - snapshotGroupRefs 引用的 groupUuid 必须存在于 snapshotGroups 中
+ *
+ */
+public class VmInstanceMetadataValidator {
+
+ private VmInstanceMetadataValidator() {
+ }
+
+ /**
+ * 执行全量校验。
+ *
+ * @param dto 待校验的元数据 DTO
+ * @param currentVersion 当前平台 schema 版本
+ * @throws CloudRuntimeException 校验失败时抛出
+ */
+ public static void validate(VmInstanceMetadataDTO dto, String currentVersion) {
+ validateSchemaVersion(dto, currentVersion);
+ validateResourceUuidConsistency(dto);
+ validateSnapshotGroupIntegrity(dto);
+ }
+
+ /**
+ * 校验 schema 版本是否匹配当前平台版本。
+ *
+ * @param dto 待校验的元数据 DTO
+ * @param currentVersion 当前平台 schema 版本
+ * @throws CloudRuntimeException 版本缺失或不匹配时抛出
+ */
+ public static void validateSchemaVersion(VmInstanceMetadataDTO dto, String currentVersion) {
+ if (dto.schemaVersion == null || dto.schemaVersion.isEmpty()) {
+ throw new CloudRuntimeException("metadata schemaVersion is missing");
+ }
+ if (!dto.schemaVersion.equals(currentVersion)) {
+ throw new CloudRuntimeException(String.format(
+ "metadata schemaVersion[%s] does not match current platform version[%s]," +
+ " please upgrade metadata first",
+ dto.schemaVersion, currentVersion));
+ }
+ }
+
+ /**
+ * 校验所有 ResourceMetadata 的 resourceUuid 与 vo 内部 uuid 一致。
+ *
+ * @param dto 待校验的元数据 DTO
+ * @throws CloudRuntimeException resourceUuid 缺失或与 vo.uuid 不一致时抛出
+ */
+ public static void validateResourceUuidConsistency(VmInstanceMetadataDTO dto) {
+ if (dto.vm != null) {
+ validateSingleResourceUuid(dto.vm, "vm");
+ }
+ if (dto.volumes != null) {
+ for (int i = 0; i < dto.volumes.size(); i++) {
+ validateSingleResourceUuid(dto.volumes.get(i), "volumes[" + i + "]");
+ }
+ }
+ if (dto.nics != null) {
+ for (int i = 0; i < dto.nics.size(); i++) {
+ validateSingleResourceUuid(dto.nics.get(i), "nics[" + i + "]");
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void validateSingleResourceUuid(VmInstanceMetadataDTO.ResourceMetadata rm, String path) {
+ if (rm.resourceUuid == null) {
+ throw new CloudRuntimeException(String.format(
+ "metadata %s.resourceUuid is null", path));
+ }
+ if (rm.vo == null) {
+ throw new CloudRuntimeException(String.format(
+ "metadata %s.vo is null", path));
+ }
+
+ Map voMap = JSONObjectUtil.toObject(rm.vo, Map.class);
+ Object voUuid = voMap.get("uuid");
+ if (voUuid == null) {
+ throw new CloudRuntimeException(String.format(
+ "metadata %s.vo does not contain uuid field", path));
+ }
+ if (!rm.resourceUuid.equals(voUuid.toString())) {
+ throw new CloudRuntimeException(String.format(
+ "metadata %s.resourceUuid[%s] does not match vo.uuid[%s]",
+ path, rm.resourceUuid, voUuid));
+ }
+ }
+
+ /**
+ * 校验快照组引用的完整性。
+ *
+ * snapshotGroupRefs 中引用的 volumeSnapshotGroupUuid
+ * 必须存在于 snapshotGroups 中。
+ *
+ * @param dto 待校验的元数据 DTO
+ * @throws CloudRuntimeException 引用了不存在的 group 时抛出
+ */
+ @SuppressWarnings("unchecked")
+ public static void validateSnapshotGroupIntegrity(VmInstanceMetadataDTO dto) {
+ if (dto.snapshotGroupRefs == null || dto.snapshotGroupRefs.isEmpty()) {
+ return;
+ }
+ if (dto.snapshotGroups == null || dto.snapshotGroups.isEmpty()) {
+ throw new CloudRuntimeException(
+ "metadata has snapshotGroupRefs but no snapshotGroups");
+ }
+
+ Set groupUuids = new HashSet<>();
+ for (String groupJson : dto.snapshotGroups) {
+ Map groupMap = JSONObjectUtil.toObject(groupJson, Map.class);
+ Object uuid = groupMap.get("uuid");
+ if (uuid != null) {
+ groupUuids.add(uuid.toString());
+ }
+ }
+
+ for (String refJson : dto.snapshotGroupRefs) {
+ Map refMap = JSONObjectUtil.toObject(refJson, Map.class);
+ Object groupUuid = refMap.get("volumeSnapshotGroupUuid");
+ if (groupUuid != null && !groupUuids.contains(groupUuid.toString())) {
+ throw new CloudRuntimeException(String.format(
+ "metadata snapshotGroupRef references non-existent group[uuid:%s]",
+ groupUuid));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/header/src/main/java/org/zstack/header/vm/VmMetadata.java b/header/src/main/java/org/zstack/header/vm/VmMetadata.java
new file mode 100644
index 00000000000..92452753804
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmMetadata.java
@@ -0,0 +1,44 @@
+package org.zstack.header.vm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class VmMetadata {
+ public String vmInstanceVO;
+ public List vmSystemTags = new ArrayList<>();
+ public List vmResourceConfigs = new ArrayList<>();
+
+ public List volumeVOs = new ArrayList<>();
+ // key = volumeUuid
+ // value = SystemTag
+ public Map> volumeSystemTags = new HashMap<>();
+ // key = volumeUuid
+ // value = ResourceConfig
+ public Map> volumeResourceConfigs = new HashMap<>();
+
+ public List vmNicVOs = new ArrayList<>();
+ // key = nicUuid
+ // value = SystemTag
+ public Map> vmNicSystemTags = new HashMap<>();
+ // key = nicUuid
+ // value = ResourceConfig
+ public Map> vmNicResourceConfigs = new HashMap<>();
+
+ // key = volumeUuid
+ // value = List
+ public Map> volumeSnapshots = new HashMap<>();
+
+ // VolumeSnapshotGroupVO.toString
+ public List volumeSnapshotGroupVO = new ArrayList<>();
+ // VolumeSnapshotGroupRefVO.toString
+ public List volumeSnapshotGroupRefVO = new ArrayList<>();
+
+ // key = volumeUuid
+ // value = VolumeSnapshotReferenceVO.toString
+ public Map volumeSnapshotReferenceVO = new HashMap<>();
+ // key = volumeUuid
+ // value = VolumeSnapshotReferenceTreeVO.toString
+ public Map volumeSnapshotReferenceTreeVO = new HashMap<>();
+}
diff --git a/header/src/main/java/org/zstack/header/vm/VmMetadataCanonicalEvents.java b/header/src/main/java/org/zstack/header/vm/VmMetadataCanonicalEvents.java
new file mode 100644
index 00000000000..dd5323fea7e
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmMetadataCanonicalEvents.java
@@ -0,0 +1,31 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.NeedJsonSchema;
+
+/**
+ * 虚拟机元数据相关 CanonicalEvent 定义。
+ *
+ * 通过 {@code EventFacade.fire()} 发布,供监控系统和巡检机制消费。
+ */
+public class VmMetadataCanonicalEvents {
+
+ /**
+ * GC 放弃后的 stale 事件路径。
+ *
+ * 当 {@code UpdateVmInstanceMetadataGC} 超过最大重试次数后发布此事件,
+ * {@code MetadataHealthCheckJob} 监听此事件将 VM 加入优先刷新队列。
+ */
+ public static final String VM_METADATA_STALE_PATH = "/vm/metadata/stale";
+
+ @NeedJsonSchema
+ public static class MetadataStaleData {
+ public String vmInstanceUuid;
+
+ public MetadataStaleData() {
+ }
+
+ public MetadataStaleData(String vmInstanceUuid) {
+ this.vmInstanceUuid = vmInstanceUuid;
+ }
+ }
+}
diff --git a/header/src/main/java/org/zstack/header/vm/VmMetadataDirtyVO.java b/header/src/main/java/org/zstack/header/vm/VmMetadataDirtyVO.java
new file mode 100644
index 00000000000..32e023d5031
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmMetadataDirtyVO.java
@@ -0,0 +1,132 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.managementnode.ManagementNodeVO;
+import org.zstack.header.vo.ForeignKey;
+import org.zstack.header.vo.ForeignKey.ReferenceOption;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+/**
+ * 记录 VM 元数据的"脏标记",表示该 VM 的元数据需要写入主存储。
+ *
+ * 设计要点
+ *
+ * - vmInstanceUuid 做主键:一个 VM 最多一行,天然去重。
+ * 100 个 API 只产生 1 行,不是 100 行。
+ * - managementNodeUuid FK SET_NULL:MN 宕机后 DB 约束自动释放认领,
+ * 无需额外孤儿扫描。
+ * - vmInstanceUuid FK CASCADE:VM 销毁时自动删除脏标记,无残留。
+ * - dirtyVersion:每次 markDirty +1,刷写前快照 version,
+ * 成功后比较——检测刷写期间是否有新变更。语义比时间戳比较更明确,无精度问题。
+ * - nextRetryTime:退避控制,失败后不立刻重试,等到下次重试时间。
+ *
+ *
+ * 行语义
+ *
+ * - 行存在 = VM 元数据是脏的(需要刷写)
+ * - 行不存在 = VM 元数据已是最新(或 VM 不存在)
+ * - managementNodeUuid != null = 该行已被某个 MN 认领,正在处理
+ * - managementNodeUuid == null = 该行未被认领,可被 Poller 或 triggerFlush 认领
+ *
+ */
+@Entity
+@Table(name = "VmMetadataDirtyVO")
+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 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 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/VmMetadataDirtyVO_.java b/header/src/main/java/org/zstack/header/vm/VmMetadataDirtyVO_.java
new file mode 100644
index 00000000000..8ed099d9d8e
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmMetadataDirtyVO_.java
@@ -0,0 +1,17 @@
+package org.zstack.header.vm;
+
+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 createDate;
+ public static volatile SingularAttribute lastOpDate;
+}
diff --git a/header/src/main/java/org/zstack/header/vm/VmUuidFromApiResolver.java b/header/src/main/java/org/zstack/header/vm/VmUuidFromApiResolver.java
new file mode 100644
index 00000000000..1852f716827
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/vm/VmUuidFromApiResolver.java
@@ -0,0 +1,49 @@
+package org.zstack.header.vm;
+
+import org.zstack.header.message.APIMessage;
+
+import java.util.List;
+
+/**
+ * 从 API 消息中解析关联的 vmInstanceUuid。
+ *
+ * 用于非 VM 直接 API(如 Volume/Nic/快照 API)中提取关联的 VM UUID,
+ * 以便在 API 成功后触发对应 VM 的元数据更新。
+ *
+ * 实现类示例
+ *
+ * - VolumeToVmResolver:volumeUuid → vmInstanceUuid
+ * - NicToVmResolver:vmNicUuid → vmInstanceUuid
+ * - SnapshotToVmResolver:snapshotUuid → volumeUuid → vmInstanceUuid
+ *
+ *
+ * 解析时机
+ * Resolver 应在 API 执行前 预解析 vmUuid 并缓存在上下文中,
+ * 因为 API 执行后相关资源可能已被删除(如 APIDeleteVolumeMsg 执行后 VolumeVO 不存在)。
+ *
+ * @see MetadataImpact
+ * @see UpdateVmInstanceMetadataMsg
+ */
+public interface VmUuidFromApiResolver {
+
+ /**
+ * 判断此 Resolver 是否能处理指定的 API 消息类型。
+ *
+ * @param msg API 消息
+ * @return true 表示此 Resolver 可以从该消息中解析 vmUuid
+ */
+ boolean supports(APIMessage msg);
+
+ /**
+ * 从 API 消息中解析出关联的 vmInstanceUuid 列表。
+ *
+ * 可能返回空列表(如 volume 未挂载到任何 VM)。
+ * 可能返回多个 UUID(如批量操作涉及多台 VM)。
+ *
+ * 此方法应在 API 执行前调用。
+ *
+ * @param msg API 消息
+ * @return 关联的 vmInstanceUuid 列表,不为 null
+ */
+ List resolveVmUuids(APIMessage msg);
+}
\ No newline at end of file
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
index 92e76ede2c5..51e3c4f1dee 100755
--- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
@@ -4716,4 +4716,20 @@ public void setMemoryUsage(long memoryUsage) {
this.memoryUsage = memoryUsage;
}
}
+
+ public static class WriteVmInstanceMetadataCmd extends AgentCommand {
+ public String metadata;
+ public String metadataPath;
+ }
+
+ public static class WriteVmInstanceMetadataRsp extends AgentResponse {
+ }
+
+ public static class ReadVmInstanceMetadataCmd extends AgentCommand {
+ public String metadataPath;
+ }
+
+ public static class ReadVmInstanceMetadataRsp extends AgentResponse {
+ public String metadata;
+ }
}
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
index 7cd78c36c93..6c845676ca8 100755
--- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
@@ -86,6 +86,9 @@ public interface KVMConstant {
String CLEAN_FIRMWARE_FLASH = "/clean/firmware/flash";
String FSTRIM_VM_PATH = "/vm/fstrim";
+ String WRITE_VM_INSTANCE_METADATA_PATH = "/vm/metadata/write";
+ String READ_VM_INSTANCE_METADATA_PATH = "/vm/metadata/read";
+
String ISO_TO = "kvm.isoto";
String ANSIBLE_PLAYBOOK_NAME = "kvm.py";
String ANSIBLE_MODULE_PATH = "ansible/kvm";
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
index 23d7b1cfe47..86a19c60b2f 100755
--- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
@@ -230,6 +230,8 @@ public class KVMHost extends HostBase implements Host {
private String fileDownloadPath;
private String fileUploadPath;
private String fileDownloadProgressPath;
+ private String writeVmInstanceMetadataPath;
+ private String readVmInstanceMetadataPath;
public KVMHost(KVMHostVO self, KVMHostContext context) {
super(self);
@@ -480,6 +482,14 @@ public KVMHost(KVMHostVO self, KVMHostContext context) {
ub = UriComponentsBuilder.fromHttpUrl(baseUrl);
ub.path(KVMConstant.KVM_HOST_FILE_DOWNLOAD_PROGRESS_PATH);
fileDownloadProgressPath = ub.build().toString();
+
+ ub = UriComponentsBuilder.fromHttpUrl(baseUrl);
+ ub.path(KVMConstant.WRITE_VM_INSTANCE_METADATA_PATH);
+ writeVmInstanceMetadataPath = ub.build().toString();
+
+ ub = UriComponentsBuilder.fromHttpUrl(baseUrl);
+ ub.path(KVMConstant.READ_VM_INSTANCE_METADATA_PATH);
+ readVmInstanceMetadataPath = ub.build().toString();
}
static {
@@ -738,6 +748,10 @@ protected void handleLocalMessage(Message msg) {
handle((GetFileDownloadProgressMsg) msg);
} else if (msg instanceof RestartKvmAgentMsg) {
handle((RestartKvmAgentMsg) msg);
+ } else if (msg instanceof UpdateVmInstanceMetadataOnHypervisorMsg) {
+ handle((UpdateVmInstanceMetadataOnHypervisorMsg) msg);
+ } else if (msg instanceof ReadVmInstanceMetadataOnHypervisorMsg) {
+ handle((ReadVmInstanceMetadataOnHypervisorMsg) msg);
} else {
super.handleLocalMessage(msg);
}
@@ -7309,4 +7323,77 @@ public void fail(ErrorCode errorCode) {
}
});
}
+
+ private void handle(UpdateVmInstanceMetadataOnHypervisorMsg msg) {
+ inQueue().name(String.format("update-vmInstance-metadata-on-host-%s", self.getUuid()))
+ .asyncBackup(msg)
+ .run(chain -> updateVmInstanceMetadata(msg, new NoErrorCompletion(chain) {
+ @Override
+ public void done() {
+ chain.next();
+ }
+ }));
+ }
+
+ private void updateVmInstanceMetadata(final UpdateVmInstanceMetadataOnHypervisorMsg msg, final NoErrorCompletion completion) {
+ UpdateVmInstanceMetadataOnHypervisorReply reply = new UpdateVmInstanceMetadataOnHypervisorReply();
+
+ checkStatus();
+ WriteVmInstanceMetadataCmd cmd = new WriteVmInstanceMetadataCmd();
+ cmd.metadata = msg.getMetadata();
+ cmd.metadataPath = msg.getMetadataPath();
+ new Http<>(writeVmInstanceMetadataPath, cmd, WriteVmInstanceMetadataRsp.class).call(new ReturnValueCompletion(msg) {
+ @Override
+ public void success(WriteVmInstanceMetadataRsp ret) {
+ if (!ret.isSuccess()) {
+ reply.setError(operr("operation error, because:%s", ret.getError()));
+ }
+ bus.reply(msg, reply);
+ completion.done();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ reply.setError(errorCode);
+ bus.reply(msg, reply);
+ completion.done();
+ }
+ });
+ }
+
+ private void handle(ReadVmInstanceMetadataOnHypervisorMsg msg) {
+ inQueue().name(String.format("readVmInstanceMetadata-on-host-%s", self.getUuid()))
+ .asyncBackup(msg)
+ .run(chain -> readVmInstanceMetadata(msg, new NoErrorCompletion(chain) {
+ @Override
+ public void done() {
+ chain.next();
+ }
+ }));
+ }
+
+ private void readVmInstanceMetadata(final ReadVmInstanceMetadataOnHypervisorMsg msg, final NoErrorCompletion completion) {
+ checkStatus();
+ ReadVmInstanceMetadataOnHypervisorReply reply = new ReadVmInstanceMetadataOnHypervisorReply();
+ ReadVmInstanceMetadataCmd cmd = new ReadVmInstanceMetadataCmd();
+ cmd.metadataPath = msg.getMetadataPath();
+ new Http<>(readVmInstanceMetadataPath, cmd, ReadVmInstanceMetadataRsp.class).call(new ReturnValueCompletion(msg) {
+ @Override
+ public void success(ReadVmInstanceMetadataRsp rsp) {
+ if (!rsp.isSuccess()) {
+ reply.setError(operr("operation error, because:%s", rsp.getError()));
+ }
+ reply.setMetadata(rsp.metadata);
+ bus.reply(msg, reply);
+ completion.done();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ reply.setError(errorCode);
+ bus.reply(msg, reply);
+ completion.done();
+ }
+ });
+ }
}
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..792cd13017e 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
@@ -3,6 +3,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.compute.host.VolumeMigrationTargetHostFilter;
+import org.zstack.compute.vm.VmGlobalConfig;
import org.zstack.core.asyncbatch.While;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.cloudbus.EventFacade;
@@ -902,6 +903,8 @@ public void handleLocalMessage(Message msg) {
handle((CommitVolumeSnapshotOnPrimaryStorageMsg) msg);
} else if (msg instanceof PullVolumeSnapshotOnPrimaryStorageMsg) {
handle((PullVolumeSnapshotOnPrimaryStorageMsg) msg);
+ } else if (msg instanceof UpdateVmInstanceMetadataOnPrimaryStorageMsg) {
+ handle((UpdateVmInstanceMetadataOnPrimaryStorageMsg) msg);
} else {
super.handleLocalMessage(msg);
}
@@ -3329,4 +3332,49 @@ public void fail(ErrorCode errorCode) {
public static class LocalStoragePhysicalCapacityUsage extends PrimaryStorageBase.PhysicalCapacityUsage {
public long localStorageUsedSize;
}
+
+ private void handle(final UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) {
+ // Layer 3: PS-level concurrency control (§4)
+ thdf.chainSubmit(new ChainTask(msg) {
+ @Override
+ public String getSyncSignature() {
+ return "update-metadata-on-ps-" + self.getUuid();
+ }
+
+ @Override
+ public int getSyncLevel() {
+ return VmGlobalConfig.VM_METADATA_PS_MAX_CONCURRENT.value(Integer.class);
+ }
+
+ @Override
+ public void run(SyncTaskChain chain) {
+ doHandleUpdateMetadata(msg);
+ chain.next();
+ }
+
+ @Override
+ public String getName() {
+ return "update-metadata-on-ps-" + self.getUuid();
+ }
+ });
+ }
+
+ private void doHandleUpdateMetadata(final UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) {
+ final String hostUuid = getHostUuidByResourceUuid(msg.getRootVolumeUuid());
+ LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid);
+ LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self);
+ bkd.handle(msg, hostUuid, new ReturnValueCompletion(msg) {
+ @Override
+ public void success(UpdateVmInstanceMetadataOnPrimaryStorageReply returnValue) {
+ bus.reply(msg, returnValue);
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply();
+ 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..7e85d562d8c 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.UpdateVmInstanceMetadataOnPrimaryStorageMsg;
+import org.zstack.header.vm.UpdateVmInstanceMetadataOnPrimaryStorageReply;
import org.zstack.header.volume.*;
import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageMsg;
import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageReply;
@@ -121,4 +123,6 @@ 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);
}
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..7da947744b7 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
@@ -43,10 +43,8 @@
import org.zstack.header.storage.backup.*;
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.snapshot.*;
+import org.zstack.header.vm.*;
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.volume.*;
import org.zstack.identity.AccountManager;
import org.zstack.kvm.*;
@@ -70,6 +68,7 @@
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.header.vm.VmInstanceConstant.VM_META_SUFFIX;
import static org.zstack.utils.CollectionDSL.list;
import static org.zstack.utils.CollectionUtils.transformAndRemoveNull;
@@ -3797,4 +3796,31 @@ public void fail(ErrorCode errorCode) {
}
});
}
+
+ @Override
+ void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) {
+ String installPath = Q.New(VolumeVO.class).eq(VolumeVO_.uuid, msg.getRootVolumeUuid()).select(VolumeVO_.installPath).findValue();
+ // /vms_ds/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-829a91b68e794a03865eab8a5918600a/snapshots/f2c31aeede604917aa8cee24848d8bfa.qcow2
+ // /vms_ds/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-829a91b68e794a03865eab8a5918600a/829a91b68e794a03865eab8a5918600a.qcow2
+
+ String path = installPath.replaceFirst("^(.+/vol-[^/]+/).*$", "$1");
+ String metadataPath = String.format("%s%s", path, VM_META_SUFFIX);
+
+ UpdateVmInstanceMetadataOnHypervisorMsg umsg = new UpdateVmInstanceMetadataOnHypervisorMsg();
+ umsg.setMetadata(msg.getMetadata());
+ umsg.setMetadataPath(metadataPath);
+ umsg.setHostUuid(hostUuid);
+ bus.makeTargetServiceIdByResourceUuid(umsg, HostConstant.SERVICE_ID, hostUuid);
+ bus.send(umsg, new CloudBusCallBack(msg) {
+ @Override
+ public void run(MessageReply r) {
+ UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply();
+ if (!r.isSuccess()) {
+ reply.setError(Platform.operr("failed to update vm[uuid=%s] on hypervisor.", self.getUuid())
+ .withCause(r.getError()));
+ }
+ bus.reply(msg, reply);
+ }
+ });
+ }
}
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..f179dacfeb9 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
@@ -40,10 +40,9 @@
import org.zstack.header.storage.snapshot.ShrinkVolumeSnapshotOnPrimaryStorageMsg;
import org.zstack.header.storage.snapshot.VolumeSnapshotConstant;
import org.zstack.header.storage.snapshot.VolumeSnapshotInventory;
+import org.zstack.compute.vm.VmGlobalConfig;
+import org.zstack.header.vm.*;
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.volume.*;
import org.zstack.kvm.*;
import org.zstack.storage.primary.*;
@@ -131,6 +130,8 @@ protected void handleLocalMessage(Message msg) {
handle((CommitVolumeSnapshotOnPrimaryStorageMsg) msg);
} else if (msg instanceof PullVolumeSnapshotOnPrimaryStorageMsg) {
handle((PullVolumeSnapshotOnPrimaryStorageMsg) msg);
+ } else if (msg instanceof UpdateVmInstanceMetadataOnPrimaryStorageMsg) {
+ handle((UpdateVmInstanceMetadataOnPrimaryStorageMsg) msg);
} else {
super.handleLocalMessage(msg);
}
@@ -1924,4 +1925,57 @@ private String getHostUuidFromVolume(String volumeUuid) {
return hostUuid;
}
+
+ protected void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) {
+ // Layer 3: PS-level concurrency control (§4)
+ // 同一 MN 上同一 PS 最多 N 个并发元数据写入
+ thdf.chainSubmit(new ChainTask(msg) {
+ @Override
+ public String getSyncSignature() {
+ return "update-metadata-on-ps-" + self.getUuid();
+ }
+
+ @Override
+ public int getSyncLevel() {
+ return VmGlobalConfig.VM_METADATA_PS_MAX_CONCURRENT.value(Integer.class);
+ }
+
+ @Override
+ public void run(SyncTaskChain chain) {
+ doHandleUpdateMetadata(msg);
+ chain.next();
+ }
+
+ @Override
+ public String getName() {
+ return "update-metadata-on-ps-" + self.getUuid();
+ }
+ });
+ }
+
+ private void doHandleUpdateMetadata(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) {
+ 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);
+ return;
+ }
+
+ final NfsPrimaryStorageBackend backend = getUsableBackend();
+
+ backend.handle(msg, hostUuid, new ReturnValueCompletion(msg) {
+ @Override
+ public void success(UpdateVmInstanceMetadataOnPrimaryStorageReply 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..a19f2d1d38e 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.UpdateVmInstanceMetadataOnPrimaryStorageMsg;
+import org.zstack.header.vm.UpdateVmInstanceMetadataOnPrimaryStorageReply;
import org.zstack.header.volume.VolumeStats;
import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageMsg;
import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageReply;
@@ -91,6 +93,8 @@ public interface NfsPrimaryStorageBackend {
void updateMountPoint(PrimaryStorageInventory pinv, String clusterUuid, String oldMountPoint, String newMountPoint, Completion completion);
+ void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg 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..4e0dd7309d5 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
@@ -35,10 +35,7 @@
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.snapshot.VolumeSnapshotInventory;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO;
-import org.zstack.header.vm.VmInstanceSpec;
-import org.zstack.header.vm.VmInstanceState;
-import org.zstack.header.vm.VmInstanceVO;
-import org.zstack.header.vm.VmInstanceVO_;
+import org.zstack.header.vm.*;
import org.zstack.header.volume.*;
import org.zstack.identity.AccountManager;
import org.zstack.kvm.*;
@@ -2051,4 +2048,23 @@ public void run(MessageReply r) {
}
});
}
+
+ public void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) {
+ UpdateVmInstanceMetadataOnHypervisorMsg umsg = new UpdateVmInstanceMetadataOnHypervisorMsg();
+ umsg.setMetadata(msg.getMetadata());
+ umsg.setHostUuid(hostUuid);
+ bus.makeTargetServiceIdByResourceUuid(umsg, HostConstant.SERVICE_ID, hostUuid);
+ bus.send(umsg, new CloudBusCallBack(msg) {
+ @Override
+ public void run(MessageReply r) {
+ UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply();
+ if (!r.isSuccess()) {
+ reply.setError(Platform.operr("failed to update vm[uuid=%s] metadata on hypervisor via host[uuid:%s]",
+ msg.getVmInstanceUuid(), hostUuid)
+ .withCause(r.getError()));
+ }
+ completion.success(reply);
+ }
+ });
+ }
}
diff --git a/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceAction.java b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceAction.java
new file mode 100644
index 00000000000..1fb295bf5ae
--- /dev/null
+++ b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceAction.java
@@ -0,0 +1,113 @@
+package org.zstack.sdk;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.zstack.sdk.*;
+
+public class RegisterVmInstanceAction 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.RegisterVmInstanceResult 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 clusterUuid;
+
+ @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 primaryStorageUuid;
+
+ @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false)
+ public java.lang.String hostUuid;
+
+ @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false)
+ public java.lang.String metadataPath;
+
+ @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.RegisterVmInstanceResult value = res.getResult(org.zstack.sdk.RegisterVmInstanceResult.class);
+ ret.value = value == null ? new org.zstack.sdk.RegisterVmInstanceResult() : 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/register";
+ info.needSession = true;
+ info.needPoll = true;
+ info.parameterName = "params";
+ return info;
+ }
+
+}
diff --git a/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceResult.java b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceResult.java
new file mode 100644
index 00000000000..49510a84cb9
--- /dev/null
+++ b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceResult.java
@@ -0,0 +1,14 @@
+package org.zstack.sdk;
+
+import org.zstack.sdk.VmInstanceInventory;
+
+public class RegisterVmInstanceResult {
+ public VmInstanceInventory inventory;
+ public void setInventory(VmInstanceInventory inventory) {
+ this.inventory = inventory;
+ }
+ public VmInstanceInventory getInventory() {
+ return this.inventory;
+ }
+
+}
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..869e7133c26 100755
--- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java
+++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java
@@ -14,6 +14,8 @@
import org.zstack.core.cloudbus.CloudBusListCallBack;
import org.zstack.core.cloudbus.EventFacade;
import org.zstack.core.componentloader.PluginRegistry;
+import org.zstack.core.config.GlobalConfig;
+import org.zstack.core.config.GlobalConfigDefinition;
import org.zstack.core.db.*;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
@@ -27,6 +29,7 @@
import org.zstack.core.trash.TrashType;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.core.workflow.ShareFlow;
+import org.zstack.core.workflow.ShareFlowChain;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.core.*;
import org.zstack.header.core.trash.CleanTrashResult;
@@ -49,16 +52,24 @@
import org.zstack.header.storage.primary.PrimaryStorageCanonicalEvent.PrimaryStorageDeletedData;
import org.zstack.header.storage.primary.PrimaryStorageCanonicalEvent.PrimaryStorageStatusChangedData;
import org.zstack.header.storage.snapshot.*;
+import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupRefVO;
+import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO;
+import org.zstack.header.tag.TagDefinition;
import org.zstack.header.vm.*;
import org.zstack.header.volume.*;
+import org.zstack.resourceconfig.BindResourceConfig;
import org.zstack.storage.volume.VolumeUtils;
+import org.zstack.tag.SystemTag;
+import org.zstack.utils.BeanUtils;
import org.zstack.utils.CollectionDSL;
import org.zstack.utils.DebugUtils;
import org.zstack.utils.Utils;
+import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
+import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -177,6 +188,8 @@ public void setNewAdded(boolean newAdded) {
protected abstract void handle(GetVolumeSnapshotEncryptedOnPrimaryStorageMsg msg);
+ protected abstract void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg);
+
public PrimaryStorageBase(PrimaryStorageVO self) {
this.self = self;
}
@@ -935,6 +948,10 @@ protected void handleApiMessage(APIMessage msg) {
handle((APICleanUpStorageTrashOnPrimaryStorageMsg) msg);
} else if (msg instanceof APIAddStorageProtocolMsg) {
handle((APIAddStorageProtocolMsg) msg);
+ } else if (msg instanceof APIRegisterVmInstanceMsg) {
+ handle((APIRegisterVmInstanceMsg) msg);
+ } else if (msg instanceof APIGetVmInstanceMetadataFromPrimaryStorageMsg) {
+ handle((APIGetVmInstanceMetadataFromPrimaryStorageMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
@@ -1812,4 +1829,375 @@ 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(APIRegisterVmInstanceMsg msg) {
+ APIRegisterVmInstanceReply event = new APIRegisterVmInstanceReply(msg.getId());
+ thdf.chainSubmit(new ChainTask(msg) {
+ @Override
+ public String getSyncSignature() {
+ return String.format("register-vm-from-%s", msg.getMetadataPath());
+ }
+
+ @Override
+ public void run(SyncTaskChain chain) {
+ registerVmInstance(msg, new ReturnValueCompletion(chain, msg) {
+ @Override
+ public void success(VmInstanceInventory vmInstanceInventory) {
+ event.setInventory(vmInstanceInventory);
+ bus.publish(event);
+ chain.next();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ bus.publish(event);
+ chain.next();
+ }
+ });
+ }
+
+ @Override
+ public String getName() {
+ return String.format("register-vm-from-%s", msg.getMetadataPath());
+ }
+ });
+ }
+
+ private void registerVmInstance(APIRegisterVmInstanceMsg msg, ReturnValueCompletion completion) {
+ FlowChain chain = new ShareFlowChain();
+ chain.setName("register-vm-from-metadata");
+ chain.then(new ShareFlow() {
+ VmMetadata vmMetadata;
+ VmInstanceInventory vmInstanceInventory;
+
+ @Override
+ public void setup() {
+ flow(new NoRollbackFlow() {
+ String __name__ = "read-metadata";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ ReadVmInstanceMetadataOnHypervisorMsg umsg = new ReadVmInstanceMetadataOnHypervisorMsg();
+ umsg.setHostUuid(msg.getHostUuid());
+ umsg.setMetadataPath(msg.getMetadataPath());
+ bus.makeTargetServiceIdByResourceUuid(umsg, HostConstant.SERVICE_ID, msg.getHostUuid());
+ bus.send(umsg, new CloudBusCallBack(msg) {
+ @Override
+ public void run(MessageReply r) {
+ if (!r.isSuccess()) {
+ trigger.fail(operr("failed to update vm[uuid=%s] on hypervisor.",
+ self.getUuid()).withCause(r.getError()));
+ return;
+ }
+ ReadVmInstanceMetadataOnHypervisorReply reply = r.castReply();
+ vmMetadata = JSONObjectUtil.toObject(reply.getMetadata(), VmMetadata.class);
+ trigger.next();
+ }
+ });
+ }
+ });
+
+ flow(new NoRollbackFlow() {
+ String __name__ = "register-volume";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ List volumesString = vmMetadata.volumeVOs;
+
+ List volumes = new ArrayList<>();
+ volumesString.forEach(v -> volumes.add(JSONObjectUtil.toObject(v, VolumeVO.class)));
+
+ List newVolumes = new ArrayList<>();
+ volumes.forEach(v -> {
+ VolumeVO vo = new VolumeVO();
+// vo.setRootImageUuid(vo.getRootImageUuid());
+ vo.setAccountUuid(msg.getSession().getAccountUuid());
+ vo.setPrimaryStorageUuid(msg.getPrimaryStorageUuid());
+ vo.setInstallPath(v.getInstallPath());
+
+ vo.setCreateDate(v.getCreateDate());
+ vo.setDescription(v.getDescription());
+ vo.setName(v.getName());
+ vo.setSize(v.getSize());
+ vo.setActualSize(v.getActualSize());
+ vo.setState(v.getState());
+ vo.setUuid(v.getUuid());
+ vo.setVmInstanceUuid(v.getVmInstanceUuid());
+ vo.setType(v.getType());
+ vo.setCreateDate(v.getCreateDate());
+ vo.setLastOpDate(v.getLastOpDate());
+ vo.setDeviceId(v.getDeviceId());
+ vo.setStatus(v.getStatus());
+ vo.setFormat(v.getFormat());
+ vo.setShareable(v.isShareable());
+ vo.setVolumeQos(v.getVolumeQos());
+ vo.setLastDetachDate(v.getLastDetachDate());
+ vo.setLastVmInstanceUuid(v.getLastVmInstanceUuid());
+ vo.setLastAttachDate(v.getLastAttachDate());
+ vo.setProtocol(v.getProtocol());
+ newVolumes.add(vo);
+ });
+ dbf.persistCollection(newVolumes);
+ trigger.next();
+ }
+ });
+
+ flow(new NoRollbackFlow() {
+ String __name__ = "register-snapshot";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ // 快照
+ vmMetadata.volumeSnapshots.forEach((volumeUuid, snapshotList) -> {
+ // 一个 volume 有多个快照树
+ // key = treeuuid
+ // value = snapshosts
+ Map> snapshotsByTreeUuid = new HashMap<>();
+ snapshotList.forEach(snapshot -> {
+ VolumeSnapshotInventory inv = JSONObjectUtil.toObject(snapshot, VolumeSnapshotInventory.class);
+ if (snapshotsByTreeUuid.containsKey(inv.getTreeUuid())) {
+ snapshotsByTreeUuid.get(inv.getTreeUuid()).add(inv);
+ } else {
+ snapshotsByTreeUuid.put(inv.getTreeUuid(), new ArrayList<>());
+ snapshotsByTreeUuid.get(inv.getTreeUuid()).add(inv);
+ }
+ });
+
+ // 遍历每一颗树
+ snapshotsByTreeUuid.forEach((treeUuid, snapshots) -> {
+ //构建快照树
+ VolumeSnapshotTree tree = VolumeSnapshotTree.fromInventories(snapshots);
+ // 层级遍历 快照
+ List levelOrderTraversals = tree.levelOrderTraversal();
+ // 判断当前树有没有 latest 节点
+ boolean treeIsCurrent = levelOrderTraversals.stream().anyMatch(VolumeSnapshotInventory::isLatest);
+
+ // 先创建快照树,VolumeSnapshotVO 外键依赖 VolumeSnapshotTreeVO
+ VolumeSnapshotTreeVO newTree = new VolumeSnapshotTreeVO();
+ newTree.setCurrent(treeIsCurrent);
+ newTree.setVolumeUuid(volumeUuid);
+ newTree.setUuid(treeUuid);
+ newTree.setStatus(VolumeSnapshotTreeStatus.Completed);
+ dbf.persist(newTree);
+
+ // 按照层级遍历的快照构建VolumeSnapshotTreeVO
+ levelOrderTraversals.forEach(snapshot -> {
+ VolumeSnapshotVO vo = new VolumeSnapshotVO();
+ vo.setPrimaryStorageUuid(msg.getPrimaryStorageUuid());
+ vo.setPrimaryStorageInstallPath(snapshot.getPrimaryStorageInstallPath());
+
+ vo.setName(snapshot.getName());
+ vo.setCreateDate(snapshot.getCreateDate());
+ vo.setDescription(snapshot.getDescription());
+ vo.setLastOpDate(snapshot.getLastOpDate());
+ vo.setParentUuid(snapshot.getParentUuid());
+ vo.setState(VolumeSnapshotState.valueOf(snapshot.getState()));
+ vo.setType(snapshot.getType());
+ vo.setVolumeUuid(snapshot.getVolumeUuid());
+ vo.setFormat(snapshot.getFormat());
+ vo.setUuid(snapshot.getUuid());
+ vo.setStatus(VolumeSnapshotStatus.valueOf(snapshot.getStatus()));
+ vo.setLatest(snapshot.isLatest());
+ vo.setSize(snapshot.getSize());
+ vo.setVolumeType(snapshot.getVolumeType());
+ vo.setTreeUuid(snapshot.getTreeUuid());
+ vo.setDistance(snapshot.getDistance());
+ dbf.persist(vo);
+ });
+ });
+ });
+
+ // 快照组
+ List newGroups = new ArrayList<>();
+ vmMetadata.volumeSnapshotGroupVO.forEach(group -> {
+ VolumeSnapshotGroupVO vo = JSONObjectUtil.toObject(group, VolumeSnapshotGroupVO.class);
+ vo.setAccountUuid(msg.getSession().getAccountUuid());
+ newGroups.add(vo);
+ });
+ dbf.persistCollection(newGroups);
+
+ // 快照组ref
+ List newGroupRefs = new ArrayList<>();
+ vmMetadata.volumeSnapshotGroupRefVO.forEach(group -> {
+ VolumeSnapshotGroupRefVO vo = JSONObjectUtil.toObject(group, VolumeSnapshotGroupRefVO.class);
+ newGroupRefs.add(vo);
+ });
+ dbf.persistCollection(newGroupRefs);
+
+ trigger.next();
+ }
+ });
+
+ flow(new NoRollbackFlow() {
+ String __name__ = "register-vmInstance";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ VmInstanceVO metaVm = JSONObjectUtil.toObject(vmMetadata.vmInstanceVO, VmInstanceVO.class);
+ VmInstanceVO newVm = new VmInstanceVO();
+
+ newVm.setClusterUuid(msg.getClusterUuid());
+ newVm.setHostUuid(msg.getHostUuid());
+ // 寻找有没有cache的tag lv 构建imageCache
+// newVm.setImageUuid();
+
+ newVm.setUuid(metaVm.getUuid());
+ newVm.setName(metaVm.getName());
+ newVm.setDescription(metaVm.getDescription());
+ newVm.setType(metaVm.getType());
+ newVm.setHypervisorType(metaVm.getHypervisorType());
+ newVm.setCreateDate(metaVm.getCreateDate());
+ newVm.setLastOpDate(metaVm.getLastOpDate());
+ newVm.setState(metaVm.getState());
+ newVm.setRootVolumeUuid(metaVm.getRootVolumeUuid());
+ newVm.setInternalId(metaVm.getInternalId());
+ newVm.setCpuNum(metaVm.getCpuNum());
+ newVm.setCpuSpeed(metaVm.getCpuSpeed());
+ newVm.setMemorySize(metaVm.getMemorySize());
+ newVm.setReservedMemorySize(metaVm.getReservedMemorySize());
+ newVm.setAllocatorStrategy(metaVm.getAllocatorStrategy());
+ newVm.setPlatform(metaVm.getPlatform());
+ newVm.setArchitecture(metaVm.getArchitecture());
+ newVm.setGuestOsType(metaVm.getGuestOsType());
+ dbf.persist(newVm);
+ vmInstanceInventory = VmInstanceInventory.valueOf(newVm);
+ trigger.next();
+// List vmSystemTags = vmMetadata.vmSystemTags;
+// List vmResourceConfigs = vmMetadata.vmResourceConfigs;
+//
+// try {
+// List systemTags = getResourceSystemTagFromSystem(VmInstanceVO.class.getSimpleName());
+// List resourceConfigs = getResourceConfigFromSystem(VmInstanceVO.class.getSimpleName());
+//
+// List tagVOS = new ArrayList<>();
+// vmSystemTags.forEach(tag -> {
+// List info = asList(tag.split("_"));
+// String t = info.get(0);
+// Boolean inherent = Boolean.valueOf(info.get(1));
+// String type = info.get(2);
+// systemTags.forEach(it -> {
+// if (!it.isMatch(t)) {
+// return;
+// }
+// SystemTagVO vo = new SystemTagVO();
+// vo.setTag(t);
+// vo.setType(TagType.valueOf(type));
+// vo.setInherent(inherent);
+// vo.setResourceType(VmInstanceVO.class.getSimpleName());
+// vo.setResourceUuid(newVm.getUuid());
+// tagVOS.add(vo);
+// });
+// });
+//
+// List configVOS = new ArrayList<>();
+// vmResourceConfigs.forEach(tag -> {
+// List info = asList(tag.split("_"));
+// String identity = info.get(0);
+// String value = info.get(1);
+// resourceConfigs.forEach(it -> {
+// if (it.getIdentity() == identity) {
+// return;
+// }
+// ResourceConfigVO vo = new ResourceConfigVO();
+// vo.setCategory(identity);
+// vo.setName(identity);
+// vo.setValue(value);
+// vo.setResourceType(VmInstanceVO.class.getSimpleName());
+// vo.setResourceUuid(newVm.getUuid());
+// configVOS.add(vo);
+// });
+// });
+// } catch (IllegalAccessException | InstantiationException e) {
+// throw new RuntimeException(e);
+// }
+ }
+ });
+
+ done(new FlowDoneHandler(completion) {
+ @Override
+ public void handle(Map data) {
+ completion.success(vmInstanceInventory);
+ }
+ });
+
+ error(new FlowErrorHandler(msg) {
+ @Override
+ public void handle(ErrorCode errCode, Map data) {
+ completion.fail(errCode);
+ }
+ });
+ }
+ }).start();
+ }
+
+ private List getResourceSystemTagFromSystem(String resourceType) throws IllegalAccessException, InstantiationException {
+ List systemTags = new ArrayList<>();
+
+ Set> classes = BeanUtils.reflections.getTypesAnnotatedWith(TagDefinition.class);
+ for (Class clazz : classes) {
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ if (!SystemTag.class.isAssignableFrom(field.getType())) {
+ continue;
+ }
+
+ SystemTag systemTag = (SystemTag) field.get(clazz.newInstance());
+
+ if (resourceType.equals(systemTag.getResourceClass().getName())) {
+ systemTags.add(systemTag);
+ }
+ }
+ }
+ return systemTags;
+ }
+
+ private List getResourceConfigFromSystem(String resourceType) throws IllegalAccessException, InstantiationException {
+ List globalConfigs = new ArrayList<>();
+
+ Set> classes = BeanUtils.reflections.getTypesAnnotatedWith(GlobalConfigDefinition.class);
+ for (Class clazz : classes) {
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ if (!GlobalConfig.class.isAssignableFrom(field.getType())) {
+ continue;
+ }
+ GlobalConfig globalConfig = (GlobalConfig) field.get(clazz.newInstance());
+
+ BindResourceConfig bindResourceConfig = field.getAnnotation(BindResourceConfig.class);
+ if (bindResourceConfig == null) {
+ continue;
+ }
+
+ List bindResourceConfigs = Arrays.stream(bindResourceConfig.value()).map(Class::getName).collect(Collectors.toList());
+
+ if (bindResourceConfigs.contains(resourceType)) {
+ globalConfigs.add(globalConfig);
+ }
+ }
+ }
+
+ return globalConfigs;
+ }
+
+ private void handle(APIGetVmInstanceMetadataFromPrimaryStorageMsg msg) {
+ APIGetVmInstanceMetadataFromPrimaryStorageReply reply = new APIGetVmInstanceMetadataFromPrimaryStorageReply();
+
+ GetVmInstanceMetadataFromPrimaryStorageMsg gmsg = new GetVmInstanceMetadataFromPrimaryStorageMsg();
+ gmsg.setPrimaryStorageUuid(msg.getPrimaryStorageUuid());
+ bus.makeTargetServiceIdByResourceUuid(gmsg, PrimaryStorageConstant.SERVICE_ID, msg.getPrimaryStorageUuid());
+
+ bus.send(gmsg, new CloudBusCallBack(msg) {
+ @Override
+ public void run(MessageReply r) {
+ if (!r.isSuccess()) {
+ reply.setError(r.getError());
+ bus.reply(msg, reply);
+ return;
+ }
+ GetVmInstanceMetadataFromPrimaryStorageReply re = r.castReply();
+ reply.setVmInstanceMetadata(re.getVmInstanceMetadata());
+ bus.reply(msg, reply);
+ }
+ });
+ }
}
diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
index 07c05b73b9e..5ddb04eac93 100644
--- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
+++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
@@ -27406,6 +27406,33 @@ abstract class ApiHelper {
}
+ def registerVmInstance(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.RegisterVmInstanceAction.class) Closure c) {
+ def a = new org.zstack.sdk.RegisterVmInstanceAction()
+ 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
diff --git a/testlib/src/main/java/org/zstack/testlib/KVMSimulator.groovy b/testlib/src/main/java/org/zstack/testlib/KVMSimulator.groovy
index 94fc178245d..fcfb5a8ff78 100755
--- a/testlib/src/main/java/org/zstack/testlib/KVMSimulator.groovy
+++ b/testlib/src/main/java/org/zstack/testlib/KVMSimulator.groovy
@@ -680,5 +680,15 @@ class KVMSimulator implements Simulator {
spec.simulator(KVMConstant.KVM_UPDATE_HOSTNAME_PATH) {
return new UpdateHostnameRsp()
}
+
+ spec.simulator(KVMConstant.WRITE_VM_INSTANCE_METADATA_PATH) { HttpEntity e ->
+ return new WriteVmInstanceMetadataRsp()
+ }
+
+ spec.simulator(KVMConstant.READ_VM_INSTANCE_METADATA_PATH) { HttpEntity e ->
+ def rsp = new ReadVmInstanceMetadataRsp()
+ rsp.metadata = "{\"vmInstanceVO\":\"{\\\"vmNics\\\":[{\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"l3NetworkUuid\\\":\\\"28d3a9c8e54c48f290ab4f9e52bbb006\\\",\\\"mac\\\":\\\"fa:81:16:b2:32:00\\\",\\\"hypervisorType\\\":\\\"KVM\\\",\\\"deviceId\\\":0,\\\"internalName\\\":\\\"vnic1.0\\\",\\\"driverType\\\":\\\"virtio\\\",\\\"type\\\":\\\"VNIC\\\",\\\"state\\\":\\\"enable\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"usedIps\\\":[],\\\"uuid\\\":\\\"a77234a5a45a4a7caca46d01d746f41f\\\",\\\"resourceType\\\":\\\"VmNicVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.vm.VmNicVO\\\"}],\\\"allVolumes\\\":[{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":2,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":2,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"},{\\\"name\\\":\\\"ROOT-for-vmName\\\",\\\"description\\\":\\\"Root volume for VM[uuid:77bc3074f5f4438c836ce6c56bc5a4aa]\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"rootImageUuid\\\":\\\"575591e021b446e4b465e981da3a8d1b\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"type\\\":\\\"Root\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":0,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"ROOT-for-vmName\\\",\\\"description\\\":\\\"Root volume for VM[uuid:77bc3074f5f4438c836ce6c56bc5a4aa]\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"rootImageUuid\\\":\\\"575591e021b446e4b465e981da3a8d1b\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"type\\\":\\\"Root\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":0,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceName\\\":\\\"ROOT-for-vmName\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"},{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/43436624dc714282913e0a141246629e\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":1,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/43436624dc714282913e0a141246629e\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":1,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"},{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":3,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":3,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"}],\\\"vmCdRoms\\\":[{\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"deviceId\\\":0,\\\"name\\\":\\\"vm-77bc3074f5f4438c836ce6c56bc5a4aa-cdRom\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"uuid\\\":\\\"e8a57f5b8c834573b4da822b672740e4\\\",\\\"resourceName\\\":\\\"vm-77bc3074f5f4438c836ce6c56bc5a4aa-cdRom\\\",\\\"resourceType\\\":\\\"VmCdRomVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.vm.cdrom.VmCdRomVO\\\"}],\\\"name\\\":\\\"vmName\\\",\\\"zoneUuid\\\":\\\"d71de3f6981d46c9a2be43e5fcf31021\\\",\\\"clusterUuid\\\":\\\"29f13acb820d4f7f8cd3593b79b742e5\\\",\\\"imageUuid\\\":\\\"575591e021b446e4b465e981da3a8d1b\\\",\\\"hostUuid\\\":\\\"e99debc09c5845fb8ed682320117f4ce\\\",\\\"internalId\\\":1,\\\"lastHostUuid\\\":\\\"e99debc09c5845fb8ed682320117f4ce\\\",\\\"rootVolumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"defaultL3NetworkUuid\\\":\\\"28d3a9c8e54c48f290ab4f9e52bbb006\\\",\\\"type\\\":\\\"UserVm\\\",\\\"hypervisorType\\\":\\\"KVM\\\",\\\"cpuNum\\\":1,\\\"cpuSpeed\\\":0,\\\"memorySize\\\":1073741824,\\\"reservedMemorySize\\\":0,\\\"platform\\\":\\\"Linux\\\",\\\"architecture\\\":\\\"x86_64\\\",\\\"guestOsType\\\":\\\"CentOS\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:45 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"state\\\":\\\"Running\\\",\\\"uuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceName\\\":\\\"vmName\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.vm.VmInstanceVO\\\"}\",\"vmSystemTags\":[\"{\\\"inherent\\\":false,\\\"uuid\\\":\\\"38a9b4bd1b8b3dfa829d582aafb2ec25\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"tag\\\":\\\"syncPorts::77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:45 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:45 AM\\\"}\",\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"3e984cdb5edb47559a3f907e1d49bfcc\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"tag\\\":\\\"additionalQmp\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\",\"{\\\"inherent\\\":false,\\\"uuid\\\":\\\"85237d3a06133523bd84669349040ec5\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"tag\\\":\\\"vmPriority::Normal\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\",\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"b7c5d5e94ba13159ab2c8c65c1d7bc29\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"tag\\\":\\\"vmSystemSerialNumber::8ed14f00-50bb-4e9e-9448-e92c0f67e1e1\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\",\"{\\\"inherent\\\":false,\\\"uuid\\\":\\\"d5019730aeba3e57b2f1a3e8d74d0cbc\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"tag\\\":\\\"ha::None\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\"],\"vmResourceConfigs\":[\"{\\\"uuid\\\":\\\"8d2f9937a28846aba03fded826c10c73\\\",\\\"resourceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"resourceType\\\":\\\"VmInstanceVO\\\",\\\"name\\\":\\\"nicMultiQueueNum\\\",\\\"description\\\":\\\"default num of queues on virtio nic\\\",\\\"category\\\":\\\"vm\\\",\\\"value\\\":\\\"1\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\"],\"volumeVOs\":[\"{\\\"name\\\":\\\"ROOT-for-vmName\\\",\\\"description\\\":\\\"Root volume for VM[uuid:77bc3074f5f4438c836ce6c56bc5a4aa]\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"rootImageUuid\\\":\\\"575591e021b446e4b465e981da3a8d1b\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"type\\\":\\\"Root\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":0,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"ROOT-for-vmName\\\",\\\"description\\\":\\\"Root volume for VM[uuid:77bc3074f5f4438c836ce6c56bc5a4aa]\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"rootImageUuid\\\":\\\"575591e021b446e4b465e981da3a8d1b\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"type\\\":\\\"Root\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":0,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceName\\\":\\\"ROOT-for-vmName\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"}\",\"{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":3,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":3,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"}\",\"{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":2,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":2,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"}\",\"{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/43436624dc714282913e0a141246629e\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":1,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"isShareable\\\":false,\\\"shadow\\\":{\\\"name\\\":\\\"volumeName1null\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"installPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/43436624dc714282913e0a141246629e\\\",\\\"type\\\":\\\"Data\\\",\\\"status\\\":\\\"Ready\\\",\\\"size\\\":1073741824,\\\"actualSize\\\":0,\\\"deviceId\\\":1,\\\"format\\\":\\\"qcow2\\\",\\\"state\\\":\\\"Enabled\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"isShareable\\\":false},\\\"uuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"resourceName\\\":\\\"volumeName1null\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.volume.VolumeVO\\\"}\"],\"volumeSystemTags\":{\"b7290c15276b4700af2c1b108b2b62e1\":[\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"b9874ec02b583538a5603e7eec8c5b69\\\",\\\"resourceUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"tag\\\":\\\"kvm::volume::0x000f59f934d14a68\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\"],\"8d1e76eca52647f5a4544b9ff2d370de\":[\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"96cb4b006708387b8318f0fd6ae6ab8b\\\",\\\"resourceUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"tag\\\":\\\"kvm::volume::0x000faad0c9ca4231\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:48 AM\\\"}\"],\"ae9f28cb5055498e8661793d204208ba\":[\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"5ceacd06bf753b0c8abe5bcef9b5a894\\\",\\\"resourceUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"tag\\\":\\\"kvm::volume::0x000fc4ffeaab6e71\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\"],\"db8251e870b14d60ace863a7598cce8b\":[\"{\\\"inherent\\\":true,\\\"uuid\\\":\\\"d53865baa675373a9bf07a6f501eab41\\\",\\\"resourceUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"resourceType\\\":\\\"VolumeVO\\\",\\\"tag\\\":\\\"kvm::volume::0x000fad154165d205\\\",\\\"type\\\":\\\"System\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}\"]},\"volumeResourceConfigs\":{\"b7290c15276b4700af2c1b108b2b62e1\":[],\"8d1e76eca52647f5a4544b9ff2d370de\":[],\"ae9f28cb5055498e8661793d204208ba\":[],\"db8251e870b14d60ace863a7598cce8b\":[]},\"vmNicVOs\":[\"{\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"l3NetworkUuid\\\":\\\"28d3a9c8e54c48f290ab4f9e52bbb006\\\",\\\"mac\\\":\\\"fa:81:16:b2:32:00\\\",\\\"hypervisorType\\\":\\\"KVM\\\",\\\"deviceId\\\":0,\\\"internalName\\\":\\\"vnic1.0\\\",\\\"driverType\\\":\\\"virtio\\\",\\\"type\\\":\\\"VNIC\\\",\\\"state\\\":\\\"enable\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:46 AM\\\",\\\"usedIps\\\":[],\\\"uuid\\\":\\\"a77234a5a45a4a7caca46d01d746f41f\\\",\\\"resourceType\\\":\\\"VmNicVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.vm.VmNicVO\\\"}\"],\"vmNicSystemTags\":{\"a77234a5a45a4a7caca46d01d746f41f\":[]},\"vmNicResourceConfigs\":{\"a77234a5a45a4a7caca46d01d746f41f\":[]},\"volumeSnapshots\":{\"b7290c15276b4700af2c1b108b2b62e1\":[\"{\\\"uuid\\\":\\\"7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"treeUuid\\\":\\\"f8042fb57bb04ebcb0f01bab2abeb5dd\\\",\\\"parentUuid\\\":\\\"a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":true,\\\"size\\\":1,\\\"distance\\\":4,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\"}\",\"{\\\"uuid\\\":\\\"a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"treeUuid\\\":\\\"f8042fb57bb04ebcb0f01bab2abeb5dd\\\",\\\"parentUuid\\\":\\\"b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":3,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\"}\",\"{\\\"uuid\\\":\\\"b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"treeUuid\\\":\\\"f8042fb57bb04ebcb0f01bab2abeb5dd\\\",\\\"parentUuid\\\":\\\"bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":2,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\"}\",\"{\\\"uuid\\\":\\\"bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"treeUuid\\\":\\\"f8042fb57bb04ebcb0f01bab2abeb5dd\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":1,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:51 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\"}\"],\"8d1e76eca52647f5a4544b9ff2d370de\":[\"{\\\"uuid\\\":\\\"04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"treeUuid\\\":\\\"d4a030087ed3407894c393ee81f0bc3b\\\",\\\"parentUuid\\\":\\\"79caace79a1048d58ea7c0b38815bbd0\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/79caace79a1048d58ea7c0b38815bbd0\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":0,\\\"distance\\\":3,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\"}\",\"{\\\"uuid\\\":\\\"61e2ada0170142bb8b303910a27690aa\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"treeUuid\\\":\\\"d4a030087ed3407894c393ee81f0bc3b\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":0,\\\"distance\\\":1,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:51 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\"}\",\"{\\\"uuid\\\":\\\"79caace79a1048d58ea7c0b38815bbd0\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"treeUuid\\\":\\\"d4a030087ed3407894c393ee81f0bc3b\\\",\\\"parentUuid\\\":\\\"61e2ada0170142bb8b303910a27690aa\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/61e2ada0170142bb8b303910a27690aa\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":0,\\\"distance\\\":2,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\"}\",\"{\\\"uuid\\\":\\\"bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"treeUuid\\\":\\\"d4a030087ed3407894c393ee81f0bc3b\\\",\\\"parentUuid\\\":\\\"04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":true,\\\"size\\\":0,\\\"distance\\\":4,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\"}\"],\"ae9f28cb5055498e8661793d204208ba\":[\"{\\\"uuid\\\":\\\"1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"treeUuid\\\":\\\"055b80b0727e4117b246b1b29f2d58b6\\\",\\\"parentUuid\\\":\\\"aefbe47465c047d1b118321c34425869\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/aefbe47465c047d1b118321c34425869\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":3,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\"}\",\"{\\\"uuid\\\":\\\"69e85ac72fea4263a55cbcd21785006e\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"treeUuid\\\":\\\"055b80b0727e4117b246b1b29f2d58b6\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":1,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:51 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\"}\",\"{\\\"uuid\\\":\\\"aefbe47465c047d1b118321c34425869\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"treeUuid\\\":\\\"055b80b0727e4117b246b1b29f2d58b6\\\",\\\"parentUuid\\\":\\\"69e85ac72fea4263a55cbcd21785006e\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/69e85ac72fea4263a55cbcd21785006e\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":2,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\"}\",\"{\\\"uuid\\\":\\\"b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"treeUuid\\\":\\\"055b80b0727e4117b246b1b29f2d58b6\\\",\\\"parentUuid\\\":\\\"1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":true,\\\"size\\\":1,\\\"distance\\\":4,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\"}\"],\"db8251e870b14d60ace863a7598cce8b\":[\"{\\\"uuid\\\":\\\"43436624dc714282913e0a141246629e\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"treeUuid\\\":\\\"1c0773fa98f4465b8e535ba3c00dc039\\\",\\\"parentUuid\\\":\\\"a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":true,\\\"size\\\":1,\\\"distance\\\":4,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\"}\",\"{\\\"uuid\\\":\\\"791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"treeUuid\\\":\\\"1c0773fa98f4465b8e535ba3c00dc039\\\",\\\"parentUuid\\\":\\\"a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":2,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\"}\",\"{\\\"uuid\\\":\\\"a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"treeUuid\\\":\\\"1c0773fa98f4465b8e535ba3c00dc039\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":1,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:51 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:53 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\"}\",\"{\\\"uuid\\\":\\\"a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"type\\\":\\\"Hypervisor\\\",\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"treeUuid\\\":\\\"1c0773fa98f4465b8e535ba3c00dc039\\\",\\\"parentUuid\\\":\\\"791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"primaryStorageUuid\\\":\\\"e121a11157bb4746ad3c8d56c3760a3e\\\",\\\"primaryStorageInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"format\\\":\\\"qcow2\\\",\\\"latest\\\":false,\\\"size\\\":1,\\\"distance\\\":3,\\\"state\\\":\\\"Enabled\\\",\\\"status\\\":\\\"Ready\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"backupStorageRefs\\\":[],\\\"groupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\"}\"]},\"volumeSnapshotGroupVO\":[\"{\\\"snapshotCount\\\":4,\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeSnapshotRefs\\\":[{\\\"volumeSnapshotUuid\\\":\\\"a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"69e85ac72fea4263a55cbcd21785006e\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"61e2ada0170142bb8b303910a27690aa\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}],\\\"uuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"resourceName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceType\\\":\\\"VolumeSnapshotGroupVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\\\"}\",\"{\\\"snapshotCount\\\":4,\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeSnapshotRefs\\\":[{\\\"volumeSnapshotUuid\\\":\\\"b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"79caace79a1048d58ea7c0b38815bbd0\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/61e2ada0170142bb8b303910a27690aa\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"aefbe47465c047d1b118321c34425869\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/69e85ac72fea4263a55cbcd21785006e\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}],\\\"uuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"resourceName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceType\\\":\\\"VolumeSnapshotGroupVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\\\"}\",\"{\\\"snapshotCount\\\":4,\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeSnapshotRefs\\\":[{\\\"volumeSnapshotUuid\\\":\\\"1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/aefbe47465c047d1b118321c34425869\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/79caace79a1048d58ea7c0b38815bbd0\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}],\\\"uuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"resourceName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceType\\\":\\\"VolumeSnapshotGroupVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\\\"}\",\"{\\\"snapshotCount\\\":4,\\\"name\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"vmInstanceUuid\\\":\\\"77bc3074f5f4438c836ce6c56bc5a4aa\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeSnapshotRefs\\\":[{\\\"volumeSnapshotUuid\\\":\\\"43436624dc714282913e0a141246629e\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"},{\\\"volumeSnapshotUuid\\\":\\\"7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}],\\\"uuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"resourceName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"resourceType\\\":\\\"VolumeSnapshotGroupVO\\\",\\\"concreteResourceType\\\":\\\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\\\"}\"],\"volumeSnapshotGroupRefVO\":[\"{\\\"volumeSnapshotUuid\\\":\\\"04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/79caace79a1048d58ea7c0b38815bbd0\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/aefbe47465c047d1b118321c34425869\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"43436624dc714282913e0a141246629e\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"61e2ada0170142bb8b303910a27690aa\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"69e85ac72fea4263a55cbcd21785006e\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"7832daf63d9b41d68bd1460c20ed0e0a\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"79caace79a1048d58ea7c0b38815bbd0\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/61e2ada0170142bb8b303910a27690aa\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"a31c3de68ce246538e982e0e5c7d2d73\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"a35b7ae1616a4974b2f80654c5527fbb\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"a70bb2be871644b6ad12ac8d6e9524d0\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"7648a93930db473785b0abc0e0716c1a\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":1,\\\"volumeUuid\\\":\\\"db8251e870b14d60ace863a7598cce8b\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/791f523bc99a4f08bd70b5a59d8ed5c8\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:56 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:49 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"aefbe47465c047d1b118321c34425869\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/69e85ac72fea4263a55cbcd21785006e\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"b5d771aa83584c9c88d9b84147dfc9ad\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"6db066c890d141008e8ff18bd5940d77\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:54 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"b711f22ad5c045b6ad1d770d4f301d05\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":3,\\\"volumeUuid\\\":\\\"ae9f28cb5055498e8661793d204208ba\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/1aaa92b2d1eb4c36bb8951b8e1521b34\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"bc5ab54cb3d04635923a2a9d0b5fc73f\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"d2d486455d6c472cbb7391958edebea5\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":0,\\\"volumeUuid\\\":\\\"8d1e76eca52647f5a4544b9ff2d370de\\\",\\\"volumeName\\\":\\\"ROOT-for-vmName\\\",\\\"volumeType\\\":\\\"Root\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/04ef6d31675f4ba5816b104920dc3e2c\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-ROOT-for-vmName\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:57 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:47 AM\\\"}\",\"{\\\"volumeSnapshotUuid\\\":\\\"bcc3ed8070984cd691c62f421aeaa44d\\\",\\\"volumeSnapshotGroupUuid\\\":\\\"01171364e6704dbe83c36167de52d719\\\",\\\"snapshotDeleted\\\":false,\\\"deviceId\\\":2,\\\"volumeUuid\\\":\\\"b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeName\\\":\\\"volumeName1null\\\",\\\"volumeType\\\":\\\"Data\\\",\\\"volumeSnapshotInstallPath\\\":\\\"sharedblock://e121a11157bb4746ad3c8d56c3760a3e/b7290c15276b4700af2c1b108b2b62e1\\\",\\\"volumeSnapshotName\\\":\\\"group-8d1e76eca52647f5a4544b9ff2d370de-volumeName1null\\\",\\\"createDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"lastOpDate\\\":\\\"Jan 28, 2026 4:15:52 AM\\\",\\\"volumeLastAttachDate\\\":\\\"Jan 28, 2026 4:15:50 AM\\\"}\"],\"volumeSnapshotReferenceVO\":{},\"volumeSnapshotReferenceTreeVO\":{},\"EncryptedResourceKeyRefVO\":{}}"
+ return rsp
+ }
}
}