Skip to content

Commit c60433b

Browse files
author
Zhang Wenhao
committed
<feature>[kvm]: reset TPM state on VM reimage
* Introduce RestoreVmHostFileMsg for restoring host files from snapshot group backups during revert. * Refactor VolumeSnapshotGroupBase revert flow to use SimpleFlowChain with restore-vm-host-file step. Resolves: ZSV-11441 Related: ZSV-11310 Change-Id: I716b786c7366657077686561656c6e6f696b6577
1 parent 41df743 commit c60433b

4 files changed

Lines changed: 348 additions & 37 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.zstack.header.vm.additions;
2+
3+
import org.zstack.header.message.NeedReplyMessage;
4+
import org.zstack.header.vm.VmInstanceMessage;
5+
6+
public class RestoreVmHostFileMsg extends NeedReplyMessage implements VmInstanceMessage {
7+
private String vmInstanceUuid;
8+
private String snapshotGroupUuid;
9+
10+
@Override
11+
public String getVmInstanceUuid() {
12+
return vmInstanceUuid;
13+
}
14+
15+
public void setVmInstanceUuid(String vmInstanceUuid) {
16+
this.vmInstanceUuid = vmInstanceUuid;
17+
}
18+
19+
public String getSnapshotGroupUuid() {
20+
return snapshotGroupUuid;
21+
}
22+
23+
public void setSnapshotGroupUuid(String snapshotGroupUuid) {
24+
this.snapshotGroupUuid = snapshotGroupUuid;
25+
}
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.zstack.header.vm.additions;
2+
3+
import org.zstack.header.message.MessageReply;
4+
5+
public class RestoreVmHostFileReply extends MessageReply {
6+
}

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

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.zstack.header.AbstractService;
1818
import org.zstack.header.core.ReturnValueCompletion;
1919
import org.zstack.header.core.WhileDoneCompletion;
20+
import org.zstack.header.core.workflow.Flow;
2021
import org.zstack.header.core.workflow.FlowDoneHandler;
2122
import org.zstack.header.core.workflow.FlowErrorHandler;
2223
import org.zstack.header.core.workflow.FlowTrigger;
@@ -32,11 +33,14 @@
3233
import org.zstack.header.vm.VmInstanceConstant;
3334
import org.zstack.header.vm.VmInstanceVO;
3435
import org.zstack.header.vm.VmInstanceVO_;
36+
import org.zstack.header.vm.additions.RestoreVmHostFileMsg;
37+
import org.zstack.header.vm.additions.RestoreVmHostFileReply;
3538
import org.zstack.header.vm.additions.VmHostBackupFileVO;
3639
import org.zstack.header.vm.additions.VmHostBackupFileVO_;
3740
import org.zstack.header.vm.additions.VmHostFileContentFormat;
3841
import org.zstack.header.vm.additions.VmHostFileContentVO;
3942
import org.zstack.header.vm.additions.VmHostFileContentVO_;
43+
import org.zstack.header.vm.additions.VmHostFileOperation;
4044
import org.zstack.header.vm.additions.VmHostFileType;
4145
import org.zstack.header.vm.additions.VmHostFileVO;
4246
import org.zstack.header.vm.additions.VmHostFileVO_;
@@ -56,14 +60,20 @@
5660
import java.util.Base64;
5761
import java.util.Collections;
5862
import java.util.HashMap;
63+
import java.util.HashSet;
5964
import java.util.List;
6065
import java.util.Map;
6166
import java.util.Objects;
67+
import java.util.Set;
6268

6369
import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT;
6470
import static org.zstack.compute.vm.VmGlobalConfig.RESET_TPM_AFTER_VM_CLONE;
6571
import static org.zstack.core.Platform.operr;
72+
import static org.zstack.kvm.KVMAgentCommands.*;
6673
import static org.zstack.kvm.KVMConstant.READ_VM_HOST_FILE_PATH;
74+
import static org.zstack.kvm.KVMConstant.WRITE_VM_HOST_FILE_PATH;
75+
import static org.zstack.kvm.KVMConstant.buildNvramFilePath;
76+
import static org.zstack.kvm.KVMConstant.buildTpmStateFilePath;
6777
import static org.zstack.utils.CollectionDSL.list;
6878
import static org.zstack.utils.CollectionUtils.findOneOrNull;
6979
import static org.zstack.utils.CollectionUtils.transform;
@@ -165,6 +175,8 @@ public void handleMessage(Message msg) {
165175
handle((CloneVmHostFileMsg) msg);
166176
} else if (msg instanceof BackupVmHostFileMsg) {
167177
handle((BackupVmHostFileMsg) msg);
178+
} else if (msg instanceof RestoreVmHostFileMsg) {
179+
handle((RestoreVmHostFileMsg) msg);
168180
} else {
169181
bus.dealWithUnknownMessage(msg);
170182
}
@@ -555,4 +567,231 @@ protected void scripts() {
555567
}.execute();
556568
return filesNeedPersists;
557569
}
570+
571+
private void handle(RestoreVmHostFileMsg msg) {
572+
RestoreVmHostFileReply reply = new RestoreVmHostFileReply();
573+
574+
List<VmHostBackupFileVO> backupFiles = Q.New(VmHostBackupFileVO.class)
575+
.eq(VmHostBackupFileVO_.resourceUuid, msg.getSnapshotGroupUuid())
576+
.list();
577+
578+
Tuple tuple = Q.New(VmInstanceVO.class)
579+
.select(VmInstanceVO_.hostUuid, VmInstanceVO_.lastHostUuid)
580+
.eq(VmInstanceVO_.uuid, msg.getVmInstanceUuid())
581+
.findTuple();
582+
if (tuple == null) {
583+
reply.setError(operr("VM instance [uuid:%s] not found", msg.getVmInstanceUuid()));
584+
bus.reply(msg, reply);
585+
return;
586+
}
587+
588+
String hostUuid = tuple.get(0, String.class);
589+
if (hostUuid == null) {
590+
hostUuid = tuple.get(1, String.class);
591+
}
592+
if (hostUuid == null) {
593+
reply.setError(operr("VM instance [uuid:%s] has no host", msg.getVmInstanceUuid()));
594+
bus.reply(msg, reply);
595+
return;
596+
}
597+
598+
List<VmHostFileVO> currentHostFiles = Q.New(VmHostFileVO.class)
599+
.eq(VmHostFileVO_.vmInstanceUuid, msg.getVmInstanceUuid())
600+
.eq(VmHostFileVO_.hostUuid, hostUuid)
601+
.list();
602+
603+
Map<VmHostFileType, VmHostFileVO> currentFilesByType = new HashMap<>();
604+
for (VmHostFileVO file : currentHostFiles) {
605+
currentFilesByType.put(file.getType(), file);
606+
}
607+
608+
Map<VmHostFileType, VmHostBackupFileVO> backupFilesByType = new HashMap<>();
609+
for (VmHostBackupFileVO file : backupFiles) {
610+
backupFilesByType.put(file.getType(), file);
611+
}
612+
613+
Set<VmHostFileType> allTypes = new HashSet<>();
614+
allTypes.addAll(currentFilesByType.keySet());
615+
allTypes.addAll(backupFilesByType.keySet());
616+
617+
if (allTypes.isEmpty()) {
618+
bus.reply(msg, reply);
619+
return;
620+
}
621+
622+
List<VmHostFileTO> fileList = new ArrayList<>();
623+
for (VmHostFileType type : allTypes) {
624+
VmHostFileTO to = new VmHostFileTO();
625+
to.setType(type.toString());
626+
627+
boolean hasCurrentFile = currentFilesByType.containsKey(type);
628+
boolean hasBackupFile = backupFilesByType.containsKey(type);
629+
630+
if (hasBackupFile) {
631+
// Write operation
632+
VmHostBackupFileVO backupFile = backupFilesByType.get(type);
633+
VmHostFileContentVO content = Q.New(VmHostFileContentVO.class)
634+
.eq(VmHostFileContentVO_.uuid, backupFile.getUuid())
635+
.find();
636+
if (content == null) {
637+
logger.warn(String.format("backup file content [uuid:%s] not found for type %s",
638+
backupFile.getUuid(), type));
639+
continue;
640+
}
641+
642+
if (type == VmHostFileType.NvRam) {
643+
to.setPath(buildNvramFilePath(msg.getVmInstanceUuid()));
644+
} else if (type == VmHostFileType.TpmState) {
645+
to.setPath(buildTpmStateFilePath(msg.getVmInstanceUuid()));
646+
}
647+
648+
to.setFileFormat(content.getFormat().toString());
649+
to.setOperation(VmHostFileOperation.Write.toString());
650+
String contentBase64 = Base64.getEncoder().encodeToString(content.getContent());
651+
to.setContentBase64(contentBase64);
652+
653+
fileList.add(to);
654+
} else if (hasCurrentFile) {
655+
// Delete operation
656+
VmHostFileVO currentFile = currentFilesByType.get(type);
657+
to.setPath(currentFile.getPath());
658+
to.setOperation(VmHostFileOperation.Delete.toString());
659+
660+
fileList.add(to);
661+
}
662+
}
663+
664+
if (fileList.isEmpty()) {
665+
bus.reply(msg, reply);
666+
return;
667+
}
668+
669+
final String finalHostUuid = hostUuid;
670+
SimpleFlowChain.of("restore-vm-host-file")
671+
.then(Flow.of("send-cmd")
672+
.handle(trigger -> {
673+
KVMAgentCommands.WriteVmHostFileContentCmd cmd = new KVMAgentCommands.WriteVmHostFileContentCmd();
674+
cmd.setHostFiles(fileList);
675+
676+
KvmCommandSender sender = new KvmCommandSender(finalHostUuid);
677+
sender.send(cmd, WRITE_VM_HOST_FILE_PATH, wrapper -> {
678+
KVMAgentCommands.WriteVmHostFileContentResponse writeRsp = wrapper.getResponse(KVMAgentCommands.WriteVmHostFileContentResponse.class);
679+
return writeRsp.isSuccess() ? null :
680+
operr("failed to write/delete host file response").withException(writeRsp.getError());
681+
}, new ReturnValueCompletion<KvmResponseWrapper>(msg) {
682+
@Override
683+
public void success(KvmResponseWrapper wrapper) {
684+
KVMAgentCommands.WriteVmHostFileContentResponse writeRsp = wrapper.getResponse(KVMAgentCommands.WriteVmHostFileContentResponse.class);
685+
if (writeRsp.isSuccess()) {
686+
logger.info(String.format("success to restore host files for VM[uuid:%s] from snapshot group[uuid:%s]",
687+
msg.getVmInstanceUuid(), msg.getSnapshotGroupUuid()));
688+
trigger.next();
689+
return;
690+
}
691+
trigger.fail(operr("failed to write/delete host file")
692+
.withException(writeRsp.getError()));
693+
}
694+
695+
@Override
696+
public void fail(ErrorCode errorCode) {
697+
trigger.fail(operr("failed to restore host files for VM[uuid:%s]", msg.getVmInstanceUuid())
698+
.withCause(errorCode));
699+
}
700+
});
701+
})
702+
.build())
703+
.then(Flow.of("persist-content-in-db")
704+
.handle(trigger -> {
705+
Timestamp now = Timestamp.from(Instant.now());
706+
707+
for (VmHostFileType type : allTypes) {
708+
boolean hasCurrentFile = currentFilesByType.containsKey(type);
709+
boolean hasBackupFile = backupFilesByType.containsKey(type);
710+
711+
if (hasBackupFile) {
712+
VmHostBackupFileVO backupFile = backupFilesByType.get(type);
713+
VmHostFileContentVO backupContent = Q.New(VmHostFileContentVO.class)
714+
.eq(VmHostFileContentVO_.uuid, backupFile.getUuid())
715+
.find();
716+
if (backupContent == null) {
717+
continue;
718+
}
719+
720+
if (hasCurrentFile) {
721+
// update existing VmHostFileVO and VmHostFileContentVO
722+
VmHostFileVO currentFile = currentFilesByType.get(type);
723+
SQL.New(VmHostFileVO.class)
724+
.eq(VmHostFileVO_.uuid, currentFile.getUuid())
725+
.set(VmHostFileVO_.lastOpDate, now)
726+
.update();
727+
728+
VmHostFileContentVO existingContent = Q.New(VmHostFileContentVO.class)
729+
.eq(VmHostFileContentVO_.uuid, currentFile.getUuid())
730+
.find();
731+
if (existingContent != null) {
732+
SQL.New(VmHostFileContentVO.class)
733+
.eq(VmHostFileContentVO_.uuid, currentFile.getUuid())
734+
.set(VmHostFileContentVO_.content, backupContent.getContent())
735+
.set(VmHostFileContentVO_.format, backupContent.getFormat())
736+
.set(VmHostFileContentVO_.lastOpDate, now)
737+
.update();
738+
} else {
739+
VmHostFileContentVO newContent = new VmHostFileContentVO();
740+
newContent.setUuid(currentFile.getUuid());
741+
newContent.setContent(backupContent.getContent());
742+
newContent.setFormat(backupContent.getFormat());
743+
newContent.setCreateDate(now);
744+
newContent.setLastOpDate(now);
745+
databaseFacade.persist(newContent);
746+
}
747+
} else {
748+
// create new VmHostFileVO and VmHostFileContentVO
749+
String path;
750+
if (type == VmHostFileType.NvRam) {
751+
path = buildNvramFilePath(msg.getVmInstanceUuid());
752+
} else {
753+
path = buildTpmStateFilePath(msg.getVmInstanceUuid());
754+
}
755+
756+
VmHostFileVO newFile = new VmHostFileVO();
757+
newFile.setUuid(Platform.getUuid());
758+
newFile.setVmInstanceUuid(msg.getVmInstanceUuid());
759+
newFile.setHostUuid(finalHostUuid);
760+
newFile.setType(type);
761+
newFile.setPath(path);
762+
newFile.setCreateDate(now);
763+
newFile.setLastOpDate(now);
764+
databaseFacade.persist(newFile);
765+
766+
VmHostFileContentVO newContent = new VmHostFileContentVO();
767+
newContent.setUuid(newFile.getUuid());
768+
newContent.setContent(backupContent.getContent());
769+
newContent.setFormat(backupContent.getFormat());
770+
newContent.setCreateDate(now);
771+
newContent.setLastOpDate(now);
772+
databaseFacade.persist(newContent);
773+
}
774+
} else if (hasCurrentFile) {
775+
// delete VmHostFileVO and VmHostFileContentVO
776+
VmHostFileVO currentFile = currentFilesByType.get(type);
777+
SQL.New(VmHostFileContentVO.class)
778+
.eq(VmHostFileContentVO_.uuid, currentFile.getUuid())
779+
.delete();
780+
SQL.New(VmHostFileVO.class)
781+
.eq(VmHostFileVO_.uuid, currentFile.getUuid())
782+
.delete();
783+
}
784+
}
785+
786+
trigger.next();
787+
})
788+
.build())
789+
.propagateExceptionTo(msg)
790+
.done(() -> bus.reply(msg, reply))
791+
.error(errorCode -> {
792+
reply.setError(errorCode);
793+
bus.reply(msg, reply);
794+
})
795+
.start();
796+
}
558797
}

0 commit comments

Comments
 (0)