Skip to content

Commit 51b2b37

Browse files
author
gitlab
committed
Merge branch 'zsv-ldap-2' into 'feature-zsv-5.0.0-vm-support-vtpm-and-secuceboot'
<refactor>[kvm]: reset TPM via data plane before DB cleanup See merge request zstackio/zstack!9532
2 parents b699f1f + 9c9e2e4 commit 51b2b37

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)