Skip to content
Open
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
188 changes: 141 additions & 47 deletions compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
import org.zstack.header.message.*;
import org.zstack.header.network.l3.*;
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.snapshot.VolumeSnapshotInventory;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO;
import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupRefVO;
import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO;
import org.zstack.header.vm.*;
import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicHostUuid;
import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicVmState;
Expand All @@ -71,10 +75,7 @@
import org.zstack.tag.SystemTagCreator;
import org.zstack.tag.SystemTagUtils;
import org.zstack.tag.TagManager;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.ExceptionDSL;
import org.zstack.utils.ObjectUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.*;
import org.zstack.utils.function.ForEachFunction;
import org.zstack.utils.function.Function;
import org.zstack.utils.gson.JSONObjectUtil;
Expand Down Expand Up @@ -4860,49 +4861,49 @@ public void handle(ErrorCode errCode, Map data) {
}).start();
}

private void handle(final APIRecoverVmInstanceMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return syncThreadName;
}

@Override
public void run(final SyncTaskChain chain) {
final APIRecoverVmInstanceEvent evt = new APIRecoverVmInstanceEvent(msg.getId());
refreshVO();

ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR);
if (error != null) {
evt.setError(error);
bus.publish(evt);
chain.next();
return;
}

recoverVm(new Completion(msg, chain) {
@Override
public void success() {
evt.setInventory(getSelfInventory());
bus.publish(evt);
chain.next();
}

@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
chain.next();
}
});
}

@Override
public String getName() {
return "recover-vm";
}
});
}
// private void handle(final APIRecoverVmInstanceMsg msg) {
// thdf.chainSubmit(new ChainTask(msg) {
// @Override
// public String getSyncSignature() {
// return syncThreadName;
// }
//
// @Override
// public void run(final SyncTaskChain chain) {
// final APIRecoverVmInstanceEvent evt = new APIRecoverVmInstanceEvent(msg.getId());
// refreshVO();
//
// ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR);
// if (error != null) {
// evt.setError(error);
// bus.publish(evt);
// chain.next();
// return;
// }
//
// recoverVm(new Completion(msg, chain) {
// @Override
// public void success() {
// evt.setInventory(getSelfInventory());
// bus.publish(evt);
// chain.next();
// }
//
// @Override
// public void fail(ErrorCode errorCode) {
// evt.setError(errorCode);
// bus.publish(evt);
// chain.next();
// }
// });
// }
//
// @Override
// public String getName() {
// return "recover-vm";
// }
// });
// }
Comment on lines +4864 to +4906
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

恢复 APIRecoverVmInstanceMsg 的实际执行路径,避免当前“注释实现”造成的功能回归与误导

这里把原实现整段注释掉,但 handleApiMessage() 仍然会把 APIRecoverVmInstanceMsg 路由到该 handler;结合下方新增的空实现(Line 9373-9377),当前 API recover 语义等同于被禁用,且从代码阅读角度非常容易误判“只是临时注释”。
建议:要么恢复实现(见下方建议 diff),要么明确删除路由/直接返回错误事件(避免 silent no-op)。

🤖 Prompt for AI Agents
In @compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java around lines
4863 - 4905, The handler for APIRecoverVmInstanceMsg has been fully commented
out while handleApiMessage still routes messages to it, causing a silent no-op;
either restore the original implementation in
VmInstanceBase.handle(APIRecoverVmInstanceMsg) (reintroduce the thdf.chainSubmit
ChainTask that calls validateOperationByState, refreshVO, recoverVm with the
Completion that publishes APIRecoverVmInstanceEvent via bus and uses
getSelfInventory on success), or explicitly remove the routing in
handleApiMessage and return/publish a clear error event immediately so callers
don’t get a silent success; ensure references to APIRecoverVmInstanceMsg,
APIRecoverVmInstanceEvent, validateOperationByState, recoverVm,
getSelfInventory, and thdf.chainSubmit are updated accordingly.


private void handle(final APIExpungeVmInstanceMsg msg) {
final APIExpungeVmInstanceEvent evt = new APIExpungeVmInstanceEvent(msg.getId());
Expand Down Expand Up @@ -9369,5 +9370,98 @@ public void run(MessageReply reply) {
}
});
}

static class VmMetadata {
public String vmInstanceVO;
public List<String> vmConfigs;

public String volumeVO;
public List<String> volumeConfigs;

public String vmNicVO;
public List<String> vmNicConfigs;

// value = List<VolumeSnapshotVO.toString>
public Map<String, List<String>> volumeSnapshots;

// VolumeSnapshotGroupVO.toString
public List<String> volumeSnapshotGroupVO;
// VolumeSnapshotGroupRefVO.toString
public List<String> volumeSnapshotGroupRefVO;

// value = VolumeSnapshotReferenceVO.toString
public Map<String, String> volumeSnapshotReferenceVO;
// value = VolumeSnapshotReferenceTreeVO.toString
public Map<String, String> volumeSnapshotReferenceTreeVO;
}

private void handle(final APIRecoverVmInstanceMsg msg) {
// 从 元数据的中获取
// VmInstanceVO vo1 = new VmInstanceVO();

// root 和 data volume
// VolumeVO vo2 = new VolumeVO();

// 快照 存储/tree/parent/psuuid
// final VolumeSnapshotVO vo3 = new VolumeSnapshotVO();
// 创建快照的快照树

// 快照组
// newGroup = new VolumeSnapshotGroupVO();
// newGroup.setUuid(Platform.getUuid());
// newGroup.setName(String.format("revert-vm-point-%s-%s", vmUuid, TimeUtils.getCurrentTimeStamp("yyyyMMddHHmmss")));
// newGroup.setDescription(String.format("save snapshot for revert vm [uuid:%s]", vmUuid));
// newGroup.setSnapshotCount(snapshots.size());
// newGroup.setVmInstanceUuid(vmUuid);
// newGroup.setAccountUuid(msg.getSession().getAccountUuid());
// dbf.persist(newGroup);

// 快照组ref
// VolumeSnapshotGroupRefVO ref = new VolumeSnapshotGroupRefVO();
// ref.setVolumeUuid(inv.getVolumeUuid());
// ref.setVolumeName(vols.get(inv.getVolumeUuid()).getName());
// ref.setVolumeType(inv.getVolumeType());
// ref.setVolumeSnapshotGroupUuid(group.getUuid());
// ref.setVolumeSnapshotUuid(inv.getUuid());
// ref.setVolumeSnapshotName(inv.getName());
// ref.setVolumeSnapshotInstallPath(inv.getPrimaryStorageInstallPath());
// ref.setDeviceId(vols.get(inv.getVolumeUuid()).getDeviceId());
// ref.setVolumeLastAttachDate(vols.get(inv.getVolumeUuid()).getLastAttachDate());

// 解析配置
// xml 字段 与 控制面配置的映射

// 给资源加配置

List<VmInstanceVO> vms = Q.New(VmInstanceVO.class).list();
List<VolumeVO> volumes = Q.New(VolumeVO.class).list();
List<VmNicVO> nics = Q.New(VmNicVO.class).list();

List<VolumeSnapshotVO> snapshot = Q.New(VolumeSnapshotVO.class).list();
List<VolumeSnapshotGroupVO> group = Q.New(VolumeSnapshotGroupVO.class).list();
List<VolumeSnapshotGroupRefVO> groupRef = Q.New(VolumeSnapshotGroupRefVO.class).list();

VmMetadata vmMetadata = new VmMetadata();
vmMetadata.vmInstanceVO = JSONObjectUtil.toJsonString(vms.get(0));
vmMetadata.volumeVO = JSONObjectUtil.toJsonString(volumes.get(0));
vmMetadata.vmNicVO = JSONObjectUtil.toJsonString(nics.get(0));

Map<String, List<String>> volumeSnapshots = new HashMap<>();
snapshot.forEach(s -> {
if (volumeSnapshots.containsKey(s.getVolumeUuid())) {
volumeSnapshots.get(s.getVolumeUuid()).add(JSONObjectUtil.toJsonString(VolumeSnapshotInventory.valueOf(s)));
} else {
volumeSnapshots.put(s.getVolumeUuid(), new ArrayList<>());
volumeSnapshots.get(s.getVolumeUuid()).add(JSONObjectUtil.toJsonString(VolumeSnapshotInventory.valueOf(s)));
}
});
vmMetadata.volumeSnapshots = volumeSnapshots;

vmMetadata.volumeSnapshotGroupVO = group.stream().map(JSONObjectUtil::toJsonString).collect(Collectors.toList());
vmMetadata.volumeSnapshotGroupRefVO = groupRef.stream().map(JSONObjectUtil::toJsonString).collect(Collectors.toList());

String json = JSONObjectUtil.toJsonString(vmMetadata);
logger.info(String.format("recover vm instance [uuid:%s] with metadata: %s", vms.get(0).getUuid(), json));
}
Comment on lines +9463 to +9465
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

元数据 JSON 以 info 级别输出存在敏感信息与日志放大风险。
快照/卷等 toString 往往包含路径、uuid、账号等信息,info 级别会污染审计/运维日志并引入合规风险。建议降级为 debug,做字段脱敏/截断或改为可控开关。

🤖 Prompt for AI Agents
In `@compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java` around lines
9462 - 9464, The current logger.info call in VmInstanceBase that logs
JSONObjectUtil.toJsonString(vmMetadata) along with vms.get(0).getUuid() exposes
potentially sensitive metadata and should be changed: replace the
logger.info(...) usage with a lower level (logger.debug) and/or route through a
sanitizer/truncator before serializing (e.g., build a scrubbed map from
vmMetadata removing or masking fields like paths, uuids, account identifiers and
truncating long strings) and respect a configurable flag to enable full metadata
logging; update references in VmInstanceBase around the recover log (the
JSONObjectUtil.toJsonString(vmMetadata) and logger.info call) to use the
debug-level call and sanitized JSON or gated logging via a config switch.

}