Skip to content

Commit 195a8e0

Browse files
author
Zhang Wenhao
committed
<feature>[kvm]: periodic check and sync for VM host files
Implement periodic VM host file checking with dirty detection and force sync mechanism. This improves the sync interval from 30 minutes to 15 seconds, allowing faster detection and sync of file changes. Changes: - Add changeDate and lastSyncDate fields to VmHostFileVO for tracking - Add PeriodicDirtyCheck and PeriodicForceSync sync reasons - Add VM_HOST_FILE_CHANGED canonical event for file change reporting - Update VmHostFileTracker to check files periodically and sync when changed or force sync after long period without changes - Update vm.host.file.sync.interval default from 1800 to 15 seconds - Update config description from 'syncing' to 'checking' - Implement event listener to mark files as changed when reported Resolves: ZSV-11371 Related: ZSV-11310 Change-Id: I6f62727061686b646f617463737672686d667578
1 parent 912b02f commit 195a8e0

10 files changed

Lines changed: 213 additions & 18 deletions

File tree

conf/db/zsv/V5.0.0__schema.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ CREATE TABLE IF NOT EXISTS `zstack`.`VmHostFileVO` (
1616
`type` varchar(64) NOT NULL COMMENT 'NvRam, TpmState',
1717
`path` varchar(1024) NOT NULL COMMENT 'Absolute path of the file on the host',
1818
`lastSyncReason` varchar(255) DEFAULT NULL COMMENT 'The reason for the last sync operation',
19+
`changeDate` timestamp NULL DEFAULT NULL COMMENT 'Timestamp when file was reported changed, null after sync',
20+
`lastSyncDate` timestamp NULL DEFAULT NULL COMMENT 'Timestamp of the last successful sync',
1921
`lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2022
`createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
2123
PRIMARY KEY (`uuid`),

header/src/main/java/org/zstack/header/vm/VmCanonicalEvents.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.zstack.header.message.NeedJsonSchema;
44
import org.zstack.header.errorcode.ErrorCode;
55
import java.util.Date;
6+
import java.util.List;
67
import java.time.LocalDateTime;
78

89
/**
@@ -19,6 +20,8 @@ public class VmCanonicalEvents {
1920
public static final String VM_NIC_INFO_CHANGED_PATH = "/vm/nicinfo/change";
2021
public static final String VM_NIC_INFO_DUPLICATE_PATH = "/vm/nicinfo/duplicate";
2122
public static final String VM_NIC_INFO_IPRANGE_CONFLICT_PATH = "/vm/nicinfo/iprangeConflict";
23+
public static final String VM_HOST_FILE_CHANGED_PATH = "/vm/hostfile/changed";
24+
public static final String KVM_REPORT_VM_HOST_FILE_CHANGED = "/kvm/reportvmhostfilechanged";
2225

2326
@NeedJsonSchema
2427
public static class VmCrashReportData {
@@ -317,4 +320,35 @@ public void setInternalIp(String internalIp) {
317320
this.internalIp = internalIp;
318321
}
319322
}
323+
324+
@NeedJsonSchema
325+
public static class VmHostFileChangedData {
326+
private String hostUuid;
327+
private String vmUuid;
328+
private List<String> types;
329+
330+
public String getHostUuid() {
331+
return hostUuid;
332+
}
333+
334+
public void setHostUuid(String hostUuid) {
335+
this.hostUuid = hostUuid;
336+
}
337+
338+
public String getVmUuid() {
339+
return vmUuid;
340+
}
341+
342+
public void setVmUuid(String vmUuid) {
343+
this.vmUuid = vmUuid;
344+
}
345+
346+
public List<String> getTypes() {
347+
return types;
348+
}
349+
350+
public void setTypes(List<String> types) {
351+
this.types = types;
352+
}
353+
}
320354
}

header/src/main/java/org/zstack/header/vm/additions/VmHostFileInventory.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class VmHostFileInventory {
1717
private String type;
1818
private String path;
1919
private String lastSyncReason;
20+
private Timestamp changeDate;
21+
private Timestamp lastSyncDate;
2022
private Timestamp createDate;
2123
private Timestamp lastOpDate;
2224

@@ -31,6 +33,8 @@ public static VmHostFileInventory valueOf(VmHostFileVO vo) {
3133
inv.setType(vo.getType().toString());
3234
inv.setPath(vo.getPath());
3335
inv.setLastSyncReason(vo.getLastSyncReason());
36+
inv.setChangeDate(vo.getChangeDate());
37+
inv.setLastSyncDate(vo.getLastSyncDate());
3438
inv.setCreateDate(vo.getCreateDate());
3539
inv.setLastOpDate(vo.getLastOpDate());
3640
return inv;
@@ -88,6 +92,22 @@ public void setLastSyncReason(String lastSyncReason) {
8892
this.lastSyncReason = lastSyncReason;
8993
}
9094

95+
public Timestamp getChangeDate() {
96+
return changeDate;
97+
}
98+
99+
public void setChangeDate(Timestamp changeDate) {
100+
this.changeDate = changeDate;
101+
}
102+
103+
public Timestamp getLastSyncDate() {
104+
return lastSyncDate;
105+
}
106+
107+
public void setLastSyncDate(Timestamp lastSyncDate) {
108+
this.lastSyncDate = lastSyncDate;
109+
}
110+
91111
public Timestamp getCreateDate() {
92112
return createDate;
93113
}

header/src/main/java/org/zstack/header/vm/additions/VmHostFileSyncReason.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public enum VmHostFileSyncReason {
1212
RevertSnapshot("revert snapshot"),
1313
VolumeBackup("volume backup"),
1414
BeforeHaStart("on before HA start (from last host)"),
15+
PeriodicDirtyCheck("on periodic dirty check"),
16+
PeriodicForceSync("on periodic force sync"),
1517
;
1618

1719
public final String detail;

header/src/main/java/org/zstack/header/vm/additions/VmHostFileVO.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public class VmHostFileVO extends ResourceVO {
4242
@Column
4343
private String lastSyncReason;
4444
@Column
45+
private Timestamp changeDate;
46+
@Column
47+
private Timestamp lastSyncDate;
48+
@Column
4549
private Timestamp createDate;
4650
@Column
4751
private Timestamp lastOpDate;
@@ -86,6 +90,22 @@ public void setLastSyncReason(String lastSyncReason) {
8690
this.lastSyncReason = lastSyncReason;
8791
}
8892

93+
public Timestamp getChangeDate() {
94+
return changeDate;
95+
}
96+
97+
public void setChangeDate(Timestamp changeDate) {
98+
this.changeDate = changeDate;
99+
}
100+
101+
public Timestamp getLastSyncDate() {
102+
return lastSyncDate;
103+
}
104+
105+
public void setLastSyncDate(Timestamp lastSyncDate) {
106+
this.lastSyncDate = lastSyncDate;
107+
}
108+
89109
public Timestamp getCreateDate() {
90110
return createDate;
91111
}
@@ -111,6 +131,8 @@ public String toString() {
111131
", type=" + type +
112132
", path='" + path + '\'' +
113133
", lastSyncReason='" + lastSyncReason + '\'' +
134+
", changeDate=" + changeDate +
135+
", lastSyncDate=" + lastSyncDate +
114136
", createDate=" + createDate +
115137
", lastOpDate=" + lastOpDate +
116138
'}';

header/src/main/java/org/zstack/header/vm/additions/VmHostFileVO_.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class VmHostFileVO_ extends ResourceVO_ {
1313
public static volatile SingularAttribute<VmHostFileVO, VmHostFileType> type;
1414
public static volatile SingularAttribute<VmHostFileVO, String> path;
1515
public static volatile SingularAttribute<VmHostFileVO, String> lastSyncReason;
16+
public static volatile SingularAttribute<VmHostFileVO, Timestamp> changeDate;
17+
public static volatile SingularAttribute<VmHostFileVO, Timestamp> lastSyncDate;
1618
public static volatile SingularAttribute<VmHostFileVO, Timestamp> createDate;
1719
public static volatile SingularAttribute<VmHostFileVO, Timestamp> lastOpDate;
1820
}

plugin/kvm/src/main/java/org/zstack/kvm/KVMGlobalConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ public class KVMGlobalConfig {
168168
public static GlobalConfig VM_EDK_VERSION_CONFIG = new GlobalConfig(CATEGORY, "vm.edk.version");
169169

170170
@GlobalConfigValidation(numberGreaterThan = 1, numberLessThan = 86400)
171-
@GlobalConfigDef(defaultValue = "1800", type = Long.class,
172-
description = "Interval in seconds for syncing VM host files (NvRam, TpmState) from KVM hosts")
171+
@GlobalConfigDef(defaultValue = "15", type = Long.class,
172+
description = "Interval in seconds for checking VM host files (NvRam, TpmState) on KVM hosts")
173173
public static GlobalConfig VM_HOST_FILE_SYNC_INTERVAL = new GlobalConfig(CATEGORY, "vm.host.file.sync.interval");
174174

175175
@GlobalConfigValidation(numberGreaterThan = 1, numberLessThan = 30)

plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.zstack.core.db.Q;
1313
import org.zstack.core.db.SQL;
1414
import org.zstack.core.db.SQLBatch;
15+
import org.zstack.core.timeout.TimeHelper;
1516
import org.zstack.core.workflow.SimpleFlowChain;
1617
import org.zstack.header.AbstractService;
1718
import org.zstack.header.core.ReturnValueCompletion;
@@ -101,6 +102,8 @@ public class KvmSecureBootManager extends AbstractService {
101102
private ResourceConfigFacade resourceConfigFacade;
102103
@Autowired
103104
private KvmVmHostFileFactory vmHostFileFactory;
105+
@Autowired
106+
private TimeHelper timeHelper;
104107

105108
@Override
106109
public boolean start() {
@@ -217,6 +220,7 @@ private void handle(SyncVmHostFilesFromHostMsg msg) {
217220
to.setType(VmHostFileType.NvRam.toString());
218221
cmd.getHostFiles().add(to);
219222
}
223+
long now = timeHelper.getCurrentTimeMillis();
220224

221225
SyncVmHostFilesFromHostReply reply = new SyncVmHostFilesFromHostReply();
222226
sender.send(cmd, READ_VM_HOST_FILE_PATH, wrapper -> {
@@ -237,7 +241,7 @@ public void success(KvmResponseWrapper wrapper) {
237241
if (msg.isSyncToBackup()) {
238242
error = syncToBackupFiles(msg, readRsp);
239243
} else {
240-
error = syncToHostFiles(msg, cmd, readRsp);
244+
error = syncToHostFiles(msg, cmd, readRsp, now);
241245
}
242246

243247
if (error != null) {
@@ -256,7 +260,8 @@ public void fail(ErrorCode errorCode) {
256260

257261
private ErrorCode syncToHostFiles(SyncVmHostFilesFromHostMsg msg,
258262
KVMAgentCommands.ReadVmHostFileContentCmd cmd,
259-
KVMAgentCommands.ReadVmHostFileContentResponse readRsp) {
263+
KVMAgentCommands.ReadVmHostFileContentResponse readRsp,
264+
long timeBeforeSync) {
260265
final List<VmHostFileVO> existsFiles = Q.New(VmHostFileVO.class)
261266
.eq(VmHostFileVO_.vmInstanceUuid, msg.getVmUuid())
262267
.eq(VmHostFileVO_.hostUuid, msg.getHostUuid())
@@ -272,6 +277,7 @@ private ErrorCode syncToHostFiles(SyncVmHostFilesFromHostMsg msg,
272277
existsContentUuid = Collections.emptyList();
273278
}
274279

280+
Timestamp syncTime = new Timestamp(timeBeforeSync);
275281
List<ErrorCode> errors = new ArrayList<>();
276282
for (String path : cmd.getPaths()) {
277283
KVMAgentCommands.VmHostFileTO to = findOneOrNull(readRsp.getHostFiles(), item -> item.getPath().equals(path));
@@ -291,13 +297,25 @@ private ErrorCode syncToHostFiles(SyncVmHostFilesFromHostMsg msg,
291297
VmHostFileVO file = findOneOrNull(existsFiles, item -> item.getPath().equals(path));
292298
boolean fileExists = file != null;
293299

294-
Timestamp now = Timestamp.from(Instant.now());
295300
if (fileExists) {
296-
SQL.New(VmHostFileVO.class)
297-
.eq(VmHostFileVO_.uuid, file.getUuid())
298-
.set(VmHostFileVO_.lastOpDate, now)
299-
.set(VmHostFileVO_.lastSyncReason, msg.getSyncReason())
300-
.update();
301+
String fileUuid = file.getUuid();
302+
new SQLBatch() {
303+
@Override
304+
protected void scripts() {
305+
sql(VmHostFileVO.class)
306+
.eq(VmHostFileVO_.uuid, fileUuid)
307+
.set(VmHostFileVO_.lastSyncReason, msg.getSyncReason())
308+
.set(VmHostFileVO_.lastSyncDate, syncTime)
309+
.set(VmHostFileVO_.lastOpDate, syncTime)
310+
.update();
311+
sql(VmHostFileVO.class)
312+
.eq(VmHostFileVO_.uuid, fileUuid)
313+
.lt(VmHostFileVO_.changeDate, syncTime)
314+
.set(VmHostFileVO_.changeDate, null) // CAS update
315+
.update();
316+
}
317+
}.execute();
318+
301319
} else {
302320
file = new VmHostFileVO();
303321
file.setUuid(Platform.getUuid());
@@ -306,8 +324,10 @@ private ErrorCode syncToHostFiles(SyncVmHostFilesFromHostMsg msg,
306324
file.setPath(path);
307325
file.setType(type);
308326
file.setLastSyncReason(msg.getSyncReason());
309-
file.setCreateDate(now);
310-
file.setLastOpDate(now);
327+
file.setLastSyncDate(syncTime);
328+
file.setChangeDate(null);
329+
file.setLastOpDate(syncTime);
330+
file.setCreateDate(syncTime);
311331
file.setResourceName(String.format("%s file for %s", type, msg.getVmUuid()));
312332
databaseFacade.persist(file);
313333
}
@@ -318,15 +338,15 @@ private ErrorCode syncToHostFiles(SyncVmHostFilesFromHostMsg msg,
318338
.eq(VmHostFileContentVO_.uuid, file.getUuid())
319339
.set(VmHostFileContentVO_.content, bytes)
320340
.set(VmHostFileContentVO_.format, VmHostFileContentFormat.valueOf(to.getFileFormat()))
321-
.set(VmHostFileContentVO_.lastOpDate, now)
341+
.set(VmHostFileContentVO_.lastOpDate, syncTime)
322342
.update();
323343
} else {
324344
VmHostFileContentVO content = new VmHostFileContentVO();
325345
content.setUuid(file.getUuid());
326346
content.setContent(bytes);
327347
content.setFormat(VmHostFileContentFormat.valueOf(to.getFileFormat()));
328-
content.setCreateDate(now);
329-
content.setLastOpDate(now);
348+
content.setCreateDate(syncTime);
349+
content.setLastOpDate(syncTime);
330350
databaseFacade.persist(content);
331351
}
332352

@@ -489,7 +509,7 @@ public void run(FlowTrigger trigger, Map data) {
489509
VmHostFileVO file = Q.New(VmHostFileVO.class)
490510
.eq(VmHostFileVO_.vmInstanceUuid, msg.getSrcVmUuid())
491511
.eq(VmHostFileVO_.type, type)
492-
.orderByDesc(VmHostFileVO_.lastOpDate)
512+
.orderByDesc(VmHostFileVO_.lastSyncDate)
493513
.limit(1)
494514
.find();
495515
if (file == null) {
@@ -884,6 +904,7 @@ public void fail(ErrorCode errorCode) {
884904
.eq(VmHostFileVO_.uuid, currentFile.getUuid())
885905
.set(VmHostFileVO_.lastSyncReason, Restore.reason(msg.getSyncReason()))
886906
.set(VmHostFileVO_.lastOpDate, now)
907+
.set(VmHostFileVO_.lastSyncDate, now)
887908
.update();
888909

889910
VmHostFileContentVO existingContent = contentMap.get(currentFile.getUuid());
@@ -913,6 +934,7 @@ public void fail(ErrorCode errorCode) {
913934
newFile.setType(type);
914935
newFile.setPath(buildPathForVmHostFileType(type, msg.getVmInstanceUuid()));
915936
newFile.setLastSyncReason(Restore.reason(msg.getSyncReason()));
937+
newFile.setLastSyncDate(now);
916938
newFile.setCreateDate(now);
917939
newFile.setLastOpDate(now);
918940
databaseFacade.persist(newFile);

0 commit comments

Comments
 (0)