Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.zstack.compute.vm;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.db.Q;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.message.MessageReply;
import org.zstack.header.storage.primary.CleanupVmInstanceMetadataOnPrimaryStorageMsg;
import org.zstack.header.storage.primary.PrimaryStorageConstant;
import org.zstack.header.storage.primary.PrimaryStorageVO;
import org.zstack.header.storage.primary.PrimaryStorageVO_;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.metadata.VmMetadataPathBuildExtensionPoint;
import org.zstack.header.volume.VolumeVO;
import org.zstack.header.volume.VolumeVO_;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

import java.util.Map;

@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VmExpungeMetadataFlow extends NoRollbackFlow {
private static final CLogger logger = Utils.getLogger(VmExpungeMetadataFlow.class);

@Autowired
private CloudBus bus;
@Autowired
private PluginRegistry pluginRgty;

@Override
public void run(FlowTrigger trigger, Map data) {
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
final String vmUuid = spec.getVmInventory().getUuid();

String rootVolumeUuid = spec.getVmInventory().getRootVolumeUuid();
if (rootVolumeUuid == null) {
logger.debug(String.format("[MetadataExpunge] vm[uuid:%s] has no root volume, skipping metadata cleanup", vmUuid));
trigger.next();
return;
}

String psUuid = Q.New(VolumeVO.class).eq(VolumeVO_.uuid, rootVolumeUuid).select(VolumeVO_.primaryStorageUuid).findValue();
if (psUuid == null) {
logger.debug(String.format("[MetadataExpunge] vm[uuid:%s] root volume[uuid:%s] has no primaryStorageUuid, " +
"skipping metadata cleanup", vmUuid, rootVolumeUuid));
trigger.next();
return;
}

String psType = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.type).eq(PrimaryStorageVO_.uuid, psUuid).findValue();
VmMetadataPathBuildExtensionPoint ext = pluginRgty.getExtensionFromMap(psType, VmMetadataPathBuildExtensionPoint.class);
if (ext == null) {
trigger.next();
return;
}
String metadataPath = ext.buildVmMetadataPath(psUuid, vmUuid);

CleanupVmInstanceMetadataOnPrimaryStorageMsg cmsg = new CleanupVmInstanceMetadataOnPrimaryStorageMsg();
cmsg.setPrimaryStorageUuid(psUuid);
cmsg.setVmUuid(vmUuid);
cmsg.setMetadataPath(metadataPath);
bus.makeTargetServiceIdByResourceUuid(cmsg, PrimaryStorageConstant.SERVICE_ID, psUuid);
bus.send(cmsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
logger.info(String.format("[MetadataExpunge] successfully deleted metadata for vm[uuid:%s] on ps[uuid:%s]",
vmUuid, psUuid));
} else {
// best-effort: do not fail the expunge flow, MetadataStorageOrphanDetector will clean up later
logger.warn(String.format("[MetadataExpunge] failed to delete metadata for vm[uuid:%s] on ps[uuid:%s]: %s",
vmUuid, psUuid, reply.getError()));
}
trigger.next();
}
});
}
}
83 changes: 83 additions & 0 deletions compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,87 @@ public class VmGlobalConfig {
@GlobalConfigValidation(validValues = {"None", "AuthenticAMD"})
@BindResourceConfig(value = {VmInstanceVO.class})
public static GlobalConfig VM_CPUID_VENDOR = new GlobalConfig(CATEGORY, "vm.cpuid.vendor");

@GlobalConfigValidation(validValues = {"true", "false"})
public static GlobalConfig VM_METADATA = new GlobalConfig(CATEGORY, "vm.metadata");
Comment on lines +137 to +138
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

元数据总开关的配置名和现有常量对不上。

header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataConstants.java 里已经定义了 GLOBAL_CONFIG_METADATA_ENABLED = "vm.metadata.enabled",这里却注册成了 "vm.metadata"。两边如果分别被读取/写入,会变成两个独立配置项,开关状态不会联动。请统一到同一个 key;如果这里才是正确值,也要同步改掉常量和调用方。

🛠️ 按现有常量对齐的改法
-    public static GlobalConfig VM_METADATA = new GlobalConfig(CATEGORY, "vm.metadata");
+    public static GlobalConfig VM_METADATA = new GlobalConfig(CATEGORY, "vm.metadata.enabled");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java` around lines
140 - 141, VM metadata global config key is inconsistent: VmGlobalConfig
declares VM_METADATA with key "vm.metadata" but the canonical constant
VmInstanceMetadataConstants.GLOBAL_CONFIG_METADATA_ENABLED is
"vm.metadata.enabled"; make them identical. Update the GlobalConfig
instantiation in VmGlobalConfig (VM_METADATA) to use the same key as
VmInstanceMetadataConstants.GLOBAL_CONFIG_METADATA_ENABLED (or update the
constant and all call sites if you intend "vm.metadata" to be canonical) so
reads/writes reference the same global config key.


@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_PS_MAX_CONCURRENT = new GlobalConfig(CATEGORY, "vm.metadata.ps.maxConcurrent");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_GLOBAL_MAX_CONCURRENT = new GlobalConfig(CATEGORY, "vm.metadata.global.maxConcurrent");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_GC_INITIAL_DELAY_SEC = new GlobalConfig(CATEGORY, "vm.metadata.gc.initialDelaySec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_MAX_RETRY = new GlobalConfig(CATEGORY, "vm.metadata.maxRetry");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_DIRTY_POLL_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.dirty.pollIntervalSec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_DIRTY_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.dirty.batchSize");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_UPGRADE_REFRESH_DELAY = new GlobalConfig(CATEGORY, "vm.metadata.upgrade.refreshDelaySec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_UPGRADE_REFRESH_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.upgrade.refreshBatchSize");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_NODE_LEFT_DELAY = new GlobalConfig(CATEGORY, "vm.metadata.nodeLeft.delaySec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_STALE_RECOVERY_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.staleRecovery.intervalSec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_STALE_RECOVERY_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.staleRecovery.batchSize");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_STALE_RECOVERY_MAX_CYCLES = new GlobalConfig(CATEGORY, "vm.metadata.staleRecovery.maxCycles");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_PENDING_API_TIMEOUT = new GlobalConfig(CATEGORY, "vm.metadata.pendingApi.timeoutMinutes");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_RETRY_BASE_DELAY = new GlobalConfig(CATEGORY, "vm.metadata.retry.baseDelaySeconds");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_RETRY_MAX_EXPONENT = new GlobalConfig(CATEGORY, "vm.metadata.retry.maxExponent");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_INIT_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.init.batchSize");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_INIT_BATCH_DELAY = new GlobalConfig(CATEGORY, "vm.metadata.init.batchDelaySec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_ORPHAN_CHECK_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.orphanCheck.intervalSec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_ZOMBIE_CLAIM_THRESHOLD = new GlobalConfig(CATEGORY, "vm.metadata.zombieClaim.thresholdMinutes");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_STALE_CLAIM_THRESHOLD = new GlobalConfig(CATEGORY, "vm.metadata.staleClaim.thresholdMinutes");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_TRIGGER_FLUSH_STALE = new GlobalConfig(CATEGORY, "vm.metadata.triggerFlush.staleMinutes");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_DELETE_MAX_RETRY = new GlobalConfig(CATEGORY, "vm.metadata.delete.maxRetry");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_DELETE_BASE_DELAY = new GlobalConfig(CATEGORY, "vm.metadata.delete.baseDelaySec");

public static GlobalConfig VM_METADATA_LAST_REFRESH_VERSION = new GlobalConfig(CATEGORY, "vm.metadata.lastRefreshVersion");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

VM_METADATA_LAST_REFRESH_VERSION 缺少 @GlobalConfigValidation 注解。

该字段是本文件中唯一一个没有 @GlobalConfigValidation 注解的配置项。如果这是一个需要持久化的版本号字段且不需要校验,建议添加注释说明原因以便后续维护理解。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java` at line 212,
The static GlobalConfig field VM_METADATA_LAST_REFRESH_VERSION is the only
config missing a `@GlobalConfigValidation` annotation; either annotate
VM_METADATA_LAST_REFRESH_VERSION with `@GlobalConfigValidation` (using the
existing validation pattern in this class) or, if it intentionally requires no
validation, add an inline comment on VM_METADATA_LAST_REFRESH_VERSION explaining
why validation is omitted and that the value is a persisted version number to
aid future maintainers; reference the GlobalConfigValidation annotation and the
VM_METADATA_LAST_REFRESH_VERSION symbol when applying the change.


@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_CONTENT_CHECK_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.contentCheck.intervalSec");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_CONTENT_CHECK_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.contentCheck.batchSize");

@GlobalConfigValidation(numberGreaterThan = 0)
public static GlobalConfig VM_METADATA_STORAGE_ORPHAN_CHECK_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.storageOrphanCheck.intervalSec");
}
4 changes: 4 additions & 0 deletions compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,8 @@ public String desensitizeTag(SystemTag systemTag, String tag) {
}

public static PatternedSystemTag VM_STATE_PAUSED_AFTER_MIGRATE = new PatternedSystemTag(("vmPausedAfterMigrate"), VmInstanceVO.class);

public static String VM_METADATA_REGISTERING_MN_UUID_TOKEN = "registeringMnUuid";
public static PatternedSystemTag VM_METADATA_REGISTERING_MN_UUID = new PatternedSystemTag(
String.format("vmMetadata::registeringMnUuid::{%s}", VM_METADATA_REGISTERING_MN_UUID_TOKEN), VmInstanceVO.class);
}
24 changes: 24 additions & 0 deletions conf/db/upgrade/V5.0.0__schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS `zstack`.`VmMetadataDirtyVO` (
`vmInstanceUuid` VARCHAR(32) NOT NULL,
`managementNodeUuid` VARCHAR(32) DEFAULT NULL,
`dirtyVersion` BIGINT NOT NULL DEFAULT 1,
`lastClaimTime` TIMESTAMP NULL DEFAULT NULL,
`storageStructureChange` TINYINT(1) NOT NULL DEFAULT 0,
`retryCount` INT NOT NULL DEFAULT 0,
`nextRetryTime` TIMESTAMP NULL DEFAULT NULL,
`lastOpDate` timestamp on update CURRENT_TIMESTAMP,
`createDate` timestamp,
PRIMARY KEY (`vmInstanceUuid`),
CONSTRAINT `fkVmMetadataDirtyVOVmInstanceEO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `VmInstanceEO` (`uuid`) ON DELETE CASCADE,
CONSTRAINT `fkVmMetadataDirtyVOManagementNodeVO` FOREIGN KEY (`managementNodeUuid`) REFERENCES `ManagementNodeVO` (`uuid`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `zstack`.`VmMetadataFingerprintVO` (
`vmInstanceUuid` VARCHAR(32) NOT NULL,
`metadataSnapshot` LONGTEXT,
`lastFlushTime` TIMESTAMP NULL DEFAULT NULL,
`lastFlushFailed` TINYINT(1) NOT NULL DEFAULT 0,
`staleRecoveryCount` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`vmInstanceUuid`),
CONSTRAINT `fkVmMetadataFingerprintVOVmInstanceEO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `VmInstanceEO` (`uuid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Loading