Skip to content

Commit 9c9e2e4

Browse files
author
Zhang Wenhao
committed
<refactor>[kvm]: reset TPM via data plane before DB cleanup
Refactor handle(ResetVmTpmMsg) to use SimpleFlowChain.of pattern with two flow steps: 1. Send delete commands to KVM hosts via rewriteVmHostFiles with operation=Delete, grouped by hostUuid. Failures are propagated to the caller. 2. Remove VmHostFileVO and VmHostBackupFileVO DB records only after data plane deletion succeeds. Previously this handler only deleted DB records without notifying KVM agents to remove actual files on hosts. Resolves: ZSV-11739 Related: ZSV-11310 Change-Id: I697075746b71777978747863747276736a777663
1 parent 4db6aab commit 9c9e2e4

1 file changed

Lines changed: 184 additions & 13 deletions

File tree

plugin/kvm/src/main/java/org/zstack/kvm/tpm/KvmTpmManager.java

Lines changed: 184 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.zstack.kvm.KVMAgentCommands;
6060
import org.zstack.kvm.KvmCommandSender;
6161
import org.zstack.kvm.KvmResponseWrapper;
62+
import org.zstack.kvm.efi.KvmSecureBootExtensions;
6263
import org.zstack.kvm.tpm.message.CloneVmTpmMsg;
6364
import org.zstack.kvm.tpm.message.CloneVmTpmReply;
6465
import org.zstack.resourceconfig.ResourceConfig;
@@ -98,6 +99,8 @@ public class KvmTpmManager extends AbstractService {
9899
private VmTpmManager vmTpmManager;
99100
@Autowired
100101
private TpmEncryptedResourceKeyBackend tpmKeyBackend;
102+
@Autowired
103+
private KvmSecureBootExtensions secureBootExtensions;
101104

102105
@Override
103106
public boolean start() {
@@ -514,25 +517,193 @@ public void handle(ErrorCode errCode, Map data) {
514517
}).start();
515518
}
516519

520+
static class ResetVmTpmContext {
521+
String vmInstanceUuid;
522+
523+
List<VmHostFileVO> hostFiles;
524+
VmHostFileVO hostFileToDeleteLast;
525+
List<String> hostFileUuidListDeleteSuccessfully = new ArrayList<>();
526+
ErrorCodeList errorsOnSendCmd = new ErrorCodeList();
527+
528+
static ResetVmTpmContext valueOf(ResetVmTpmMsg msg) {
529+
ResetVmTpmContext context = new ResetVmTpmContext();
530+
context.vmInstanceUuid = msg.getVmInstanceUuid();
531+
return context;
532+
}
533+
}
534+
517535
private void handle(ResetVmTpmMsg msg) {
518536
ResetVmTpmReply reply = new ResetVmTpmReply();
537+
threadFacade.chainSubmit(new ChainTask(msg) {
538+
@Override
539+
public void run(SyncTaskChain chain) {
540+
ResetVmTpmContext context = ResetVmTpmContext.valueOf(msg);
541+
resetVmTpm(context, new Completion(chain, msg) {
542+
@Override
543+
public void success() {
544+
chain.next();
545+
bus.reply(msg, reply);
546+
}
547+
548+
@Override
549+
public void fail(ErrorCode errorCode) {
550+
chain.next();
551+
reply.setError(errorCode);
552+
bus.reply(msg, reply);
553+
}
554+
});
555+
}
519556

520-
String vmUuid = msg.getVmInstanceUuid();
521-
new SQLBatch() {
522557
@Override
523-
protected void scripts() {
524-
sql(VmHostFileVO.class)
525-
.eq(VmHostFileVO_.vmInstanceUuid, vmUuid)
526-
.eq(VmHostFileVO_.type, VmHostFileType.TpmState)
527-
.delete();
528-
sql(VmHostBackupFileVO.class)
529-
.eq(VmHostBackupFileVO_.resourceUuid, vmUuid)
530-
.eq(VmHostBackupFileVO_.type, VmHostFileType.TpmState)
531-
.delete();
558+
public String getSyncSignature() {
559+
return tpmQueueSyncSignature(msg.getVmInstanceUuid());
532560
}
533-
}.execute();
534561

535-
bus.reply(msg, reply);
562+
@Override
563+
public String getName() {
564+
return "queue-of-reset-tpm-from-vm-" + msg.getVmInstanceUuid();
565+
}
566+
});
567+
}
568+
569+
private void resetVmTpm(ResetVmTpmContext context, Completion completion) {
570+
String vmUuid = context.vmInstanceUuid;
571+
572+
SimpleFlowChain.of("reset-vm-tpm-" + vmUuid)
573+
.then(Flow.of("collect-vm-host-files")
574+
.handle(trigger -> {
575+
context.hostFiles = Q.New(VmHostFileVO.class)
576+
.eq(VmHostFileVO_.vmInstanceUuid, vmUuid)
577+
.eq(VmHostFileVO_.type, VmHostFileType.TpmState)
578+
.orderByAsc(VmHostFileVO_.lastOpDate)
579+
.list();
580+
if (!context.hostFiles.isEmpty()) {
581+
// We should delete it in last turn:
582+
context.hostFileToDeleteLast = context.hostFiles.get(context.hostFiles.size() - 1);
583+
context.hostFiles.remove(context.hostFiles.size() - 1);
584+
}
585+
trigger.next();
586+
})
587+
.build())
588+
.then(Flow.of("send-delete-commands-to-hosts-exclude-last-modified")
589+
.skipIf(data -> context.hostFiles.isEmpty())
590+
.handle(trigger -> {
591+
Map<String, List<VmHostFileVO>> filesByHost = new HashMap<>();
592+
for (VmHostFileVO file : context.hostFiles) {
593+
filesByHost.computeIfAbsent(file.getHostUuid(), k -> new ArrayList<>()).add(file);
594+
}
595+
596+
new While<>(filesByHost.entrySet()).each((entry, whileCompletion) -> {
597+
List<KVMAgentCommands.VmHostFileTO> fileTOs = new ArrayList<>();
598+
for (VmHostFileVO file : entry.getValue()) {
599+
KVMAgentCommands.VmHostFileTO to = new KVMAgentCommands.VmHostFileTO();
600+
to.setPath(file.getPath());
601+
to.setType(file.getType().toString());
602+
to.setOperation(VmHostFileOperation.Delete.toString());
603+
fileTOs.add(to);
604+
}
605+
606+
KvmSecureBootExtensions.RewriteVmHostFilesContext ctx =
607+
new KvmSecureBootExtensions.RewriteVmHostFilesContext();
608+
ctx.hostUuid = entry.getKey();
609+
ctx.hostFiles = fileTOs;
610+
611+
secureBootExtensions.rewriteVmHostFiles(ctx, new Completion(whileCompletion) {
612+
@Override
613+
public void success() {
614+
context.hostFileUuidListDeleteSuccessfully.addAll(
615+
transform(entry.getValue(), VmHostFileVO::getUuid));
616+
whileCompletion.done();
617+
}
618+
619+
@Override
620+
public void fail(ErrorCode errorCode) {
621+
context.errorsOnSendCmd.add(errorCode.withOpaque("host.uuid", entry.getKey()));
622+
whileCompletion.done();
623+
}
624+
});
625+
}).run(new WhileDoneCompletion(trigger) {
626+
@Override
627+
public void done(ErrorCodeList errorCodeList) {
628+
trigger.next();
629+
}
630+
});
631+
})
632+
.build())
633+
.then(Flow.of("remove-db-records")
634+
.skipIf(data -> context.hostFileUuidListDeleteSuccessfully.isEmpty())
635+
.handle(trigger -> {
636+
SQL.New(VmHostFileVO.class)
637+
.in(VmHostFileVO_.uuid, context.hostFileUuidListDeleteSuccessfully)
638+
.delete();
639+
trigger.next();
640+
})
641+
.build())
642+
.then(Flow.of("check-if-any-error-in-command-sending")
643+
.handle(trigger -> {
644+
// If any host failed to delete, abort the chain to preserve
645+
// the last-modified TPM record as a recovery point.
646+
if (context.errorsOnSendCmd.hasError()) {
647+
if (context.errorsOnSendCmd.size() == 1) {
648+
trigger.fail(context.errorsOnSendCmd.getCauses().get(0));
649+
} else {
650+
trigger.fail(operr("failed to delete TPM files on multiple hosts")
651+
.withOpaque("vm.uuid", vmUuid)
652+
.withCause(context.errorsOnSendCmd.getCauses()));
653+
}
654+
return;
655+
}
656+
trigger.next();
657+
})
658+
.build())
659+
.then(Flow.of("send-delete-commands-to-hosts-for-last-modified")
660+
.skipIf(data -> context.hostFileToDeleteLast == null)
661+
.handle(trigger -> {
662+
KVMAgentCommands.VmHostFileTO to = new KVMAgentCommands.VmHostFileTO();
663+
to.setPath(context.hostFileToDeleteLast.getPath());
664+
to.setType(context.hostFileToDeleteLast.getType().toString());
665+
to.setOperation(VmHostFileOperation.Delete.toString());
666+
667+
KvmSecureBootExtensions.RewriteVmHostFilesContext ctx =
668+
new KvmSecureBootExtensions.RewriteVmHostFilesContext();
669+
ctx.hostUuid = context.hostFileToDeleteLast.getHostUuid();
670+
ctx.hostFiles = list(to);
671+
672+
secureBootExtensions.rewriteVmHostFiles(ctx, new Completion(trigger) {
673+
@Override
674+
public void success() {
675+
trigger.next();
676+
}
677+
678+
@Override
679+
public void fail(ErrorCode errorCode) {
680+
trigger.fail(errorCode.withOpaque("host.uuid", ctx.hostUuid));
681+
}
682+
});
683+
})
684+
.build())
685+
.then(Flow.of("remove-db-records-for-remains")
686+
.skipIf(data -> context.hostFileToDeleteLast == null)
687+
.handle(trigger -> {
688+
new SQLBatch() {
689+
@Override
690+
protected void scripts() {
691+
sql(VmHostFileVO.class)
692+
.eq(VmHostFileVO_.uuid, context.hostFileToDeleteLast.getUuid())
693+
.delete();
694+
sql(VmHostBackupFileVO.class)
695+
.eq(VmHostBackupFileVO_.resourceUuid, vmUuid)
696+
.eq(VmHostBackupFileVO_.type, VmHostFileType.TpmState)
697+
.delete();
698+
}
699+
}.execute();
700+
trigger.next();
701+
})
702+
.build())
703+
.propagateExceptionTo(completion)
704+
.done(completion::success)
705+
.error(completion::fail)
706+
.start();
536707
}
537708

538709
private void handle(APIGetTpmCapabilityMsg msg) {

0 commit comments

Comments
 (0)