3131import org .zstack .header .secret .SecretHostDefineMsg ;
3232import org .zstack .header .storage .snapshot .group .VolumeSnapshotGroupVO ;
3333import org .zstack .header .storage .snapshot .group .VolumeSnapshotGroupVO_ ;
34- import org .zstack .header .secret .SecretHostDefineReply ;
3534import org .zstack .header .secret .SecretHostGetMsg ;
3635import org .zstack .header .secret .SecretHostGetReply ;
3736import org .zstack .header .tpm .entity .TpmSpec ;
3837import org .zstack .header .tpm .entity .TpmVO ;
3938import org .zstack .header .tpm .entity .TpmVO_ ;
4039import org .zstack .header .vm .HaStartVmInstanceMsg ;
41- import org .zstack .header .vm .PreVmInstantiateResourceExtensionPoint ;
4240import org .zstack .header .vm .VmAfterExpungeExtensionPoint ;
43- import org .zstack .header .vm .VmInstanceInventory ;
4441import org .zstack .header .vm .VmInstanceMigrateExtensionPoint ;
42+ import org .zstack .header .secret .SecretHostDefineReply ;
43+ import org .zstack .header .tpm .message .RemoveTpmMsg ;
44+ import org .zstack .header .vm .PreVmInstantiateResourceExtensionPoint ;
45+ import org .zstack .header .vm .VmInstanceInventory ;
4546import org .zstack .header .vm .VmInstanceSpec ;
4647import org .zstack .header .vm .VmInstanceState ;
4748import org .zstack .header .vm .VmInstantiateResourceException ;
4849import org .zstack .header .vm .VmStateChangedExtensionPoint ;
50+ import org .zstack .header .vm .VmJustBeforeDeleteFromDbExtensionPoint ;
4951import org .zstack .header .vm .additions .VmHostBackupFileVO ;
5052import org .zstack .header .vm .additions .VmHostBackupFileVO_ ;
5153import org .zstack .header .vm .VmInstanceVO ;
6466
6567import java .sql .Timestamp ;
6668import java .time .Instant ;
69+ import java .util .HashSet ;
70+ import java .util .List ;
71+ import java .util .Set ;
72+ import java .util .concurrent .ConcurrentHashMap ;
6773import java .util .Map ;
6874
75+ import static org .zstack .header .tpm .TpmConstants .SERVICE_ID ;
6976import static org .zstack .kvm .KVMConstant .*;
7077import static org .zstack .core .Platform .operr ;
7178
7279public class KvmTpmExtensions implements KVMStartVmExtensionPoint ,
7380 PreVmInstantiateResourceExtensionPoint ,
7481 VmInstanceMigrateExtensionPoint ,
7582 VmAfterExpungeExtensionPoint ,
76- VmStateChangedExtensionPoint {
83+ VmStateChangedExtensionPoint ,
84+ VmJustBeforeDeleteFromDbExtensionPoint {
7785 private static final CLogger logger = Utils .getLogger (KvmTpmExtensions .class );
7886
7987 @ Autowired
@@ -88,6 +96,7 @@ public class KvmTpmExtensions implements KVMStartVmExtensionPoint,
8896 private CloudBus bus ;
8997
9098 private final Object hostFileLock = new Object ();
99+ private final Map <String , String > volumeMigratingSourceHostCache = new ConcurrentHashMap <>();
91100
92101 @ Override
93102 public void beforeStartVmOnKvm (KVMHostInventory host , VmInstanceSpec spec , KVMAgentCommands .StartVmCmd cmd ) {
@@ -477,6 +486,50 @@ public void fail(ErrorCode errorCode) {
477486 });
478487 }
479488
489+ @ Override
490+ public void vmJustBeforeDeleteFromDb (VmInstanceInventory inv ) {
491+ String tpmUuid = Q .New (TpmVO .class )
492+ .eq (TpmVO_ .vmInstanceUuid , inv .getUuid ())
493+ .select (TpmVO_ .uuid )
494+ .findValue ();
495+ if (tpmUuid == null ) {
496+ return ;
497+ }
498+
499+ // Delete host secrets while TPM row and key version are still resolvable.
500+ // RemoveTpm may skip or fail (e.g. VM not in Stopped), and callback order may change.
501+ Integer keyVersion = resourceKeyBackend .findKeyVersionByTpm (tpmUuid );
502+ Set <String > hostUuids = new HashSet <>();
503+ List <VmHostFileVO > tpmStateFiles = Q .New (VmHostFileVO .class )
504+ .eq (VmHostFileVO_ .vmInstanceUuid , inv .getUuid ())
505+ .eq (VmHostFileVO_ .type , VmHostFileType .TpmState )
506+ .list ();
507+ for (VmHostFileVO f : tpmStateFiles ) {
508+ if (StringUtils .isNotBlank (f .getHostUuid ())) {
509+ hostUuids .add (f .getHostUuid ());
510+ }
511+ }
512+ if (StringUtils .isNotBlank (inv .getHostUuid ())) {
513+ hostUuids .add (inv .getHostUuid ());
514+ }
515+ if (StringUtils .isNotBlank (inv .getLastHostUuid ())) {
516+ hostUuids .add (inv .getLastHostUuid ());
517+ }
518+ for (String hostUuid : hostUuids ) {
519+ deleteHostSecretBestEffort (hostUuid , inv .getUuid (), keyVersion , "vm-just-before-delete-from-db" );
520+ }
521+
522+ RemoveTpmMsg removeMsg = new RemoveTpmMsg ();
523+ removeMsg .setVmInstanceUuid (inv .getUuid ());
524+ removeMsg .setTpmUuid (tpmUuid );
525+ bus .makeTargetServiceIdByResourceUuid (removeMsg , SERVICE_ID , removeMsg .getTpmUuid ());
526+ MessageReply reply = bus .call (removeMsg );
527+ if (!reply .isSuccess ()) {
528+ logger .warn (String .format ("failed to remove TPM[uuid:%s] of VM[uuid:%s], error: %s" ,
529+ tpmUuid , inv .getUuid (), reply .getError ()));
530+ }
531+ }
532+
480533 private void clearRollbackInfo (VmInstanceSpec spec ) {
481534 if (spec .getDevicesSpec () == null || spec .getDevicesSpec ().getTpm () == null ) {
482535 return ;
@@ -530,22 +583,68 @@ public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid, NoErrorC
530583
531584 @ Override
532585 public void vmStateChanged (VmInstanceInventory vm , VmInstanceState oldState , VmInstanceState newState ) {
586+ String vmUuid = vm == null ? null : vm .getUuid ();
587+ if (StringUtils .isBlank (vmUuid )) {
588+ logger .info (String .format ("vmStateChanged skip: vmUuid is blank, oldState=%s, newState=%s" , oldState , newState ));
589+ return ;
590+ }
591+
592+ // Record source host when storage migration starts. In some end-state events (e.g. VolumeMigrating->Stopped),
593+ // inventory host fields may not carry both src/dst host values reliably.
594+ if (newState == VmInstanceState .VolumeMigrating ) {
595+ String srcHostUuid = vm .getLastHostUuid ();
596+ if (StringUtils .isBlank (srcHostUuid )) {
597+ srcHostUuid = Q .New (VmInstanceVO .class )
598+ .select (VmInstanceVO_ .lastHostUuid )
599+ .eq (VmInstanceVO_ .uuid , vmUuid )
600+ .findValue ();
601+ }
602+ if (StringUtils .isNotBlank (srcHostUuid )) {
603+ volumeMigratingSourceHostCache .put (vmUuid , srcHostUuid );
604+ logger .info (String .format (
605+ "vmStateChanged cache volume-migrating src host: vm[uuid:%s], oldState=%s, newState=%s, srcHostUuid=%s" ,
606+ vmUuid , oldState , newState , srcHostUuid ));
607+ } else {
608+ logger .info (String .format (
609+ "vmStateChanged skip cache: source host is blank, vm[uuid:%s], oldState=%s, newState=%s" ,
610+ vmUuid , oldState , newState ));
611+ }
612+ return ;
613+ }
614+
533615 if (oldState != VmInstanceState .VolumeMigrating ) {
534616 return ;
535617 }
536618
537- String vmUuid = vm == null ? null : vm .getUuid ();
538- String srcHostUuid = vm == null ? null : vm .getLastHostUuid ();
539- String destHostUuid = vm == null ? null : vm .getHostUuid ();
540- if (StringUtils .isBlank (vmUuid ) || StringUtils .isBlank (srcHostUuid ) || srcHostUuid .equals (destHostUuid )) {
619+ String srcHostUuid = volumeMigratingSourceHostCache .remove (vmUuid );
620+ if (StringUtils .isBlank (srcHostUuid )) {
621+ logger .info (String .format (
622+ "vmStateChanged skip delete: no cached source host, vm[uuid:%s], oldState=%s, newState=%s" ,
623+ vmUuid , oldState , newState ));
541624 return ;
542625 }
543626
544- if (!isVmCurrentlyOnExpectedHost (vmUuid , destHostUuid )) {
627+ String destHostUuid = Q .New (VmInstanceVO .class )
628+ .select (VmInstanceVO_ .hostUuid )
629+ .eq (VmInstanceVO_ .uuid , vmUuid )
630+ .findValue ();
631+ if (StringUtils .isBlank (destHostUuid )) {
632+ destHostUuid = Q .New (VmInstanceVO .class )
633+ .select (VmInstanceVO_ .lastHostUuid )
634+ .eq (VmInstanceVO_ .uuid , vmUuid )
635+ .findValue ();
636+ }
637+ if (StringUtils .isBlank (destHostUuid ) || srcHostUuid .equals (destHostUuid )) {
638+ logger .info (String .format (
639+ "vmStateChanged skip delete: invalid host mapping, vm[uuid:%s], oldState=%s, newState=%s, srcHostUuid=%s, destHostUuid=%s" ,
640+ vmUuid , oldState , newState , srcHostUuid , destHostUuid ));
545641 return ;
546642 }
547643
548644 Integer keyVersion = findTpmKeyVersionByVmUuid (vmUuid );
645+ logger .info (String .format (
646+ "vmStateChanged trigger delete: vm[uuid:%s], srcHostUuid=%s, destHostUuid=%s, keyVersion=%s, reason=volume-migrated-host-change" ,
647+ vmUuid , srcHostUuid , destHostUuid , keyVersion ));
549648 deleteHostSecretBestEffort (srcHostUuid , vmUuid , keyVersion , "volume-migrated-host-change" );
550649 }
551650
@@ -604,9 +703,15 @@ private static boolean isVtpmSecretNotFoundOnHost(ErrorCode errorCode) {
604703
605704 private void deleteHostSecretBestEffort (String hostUuid , String vmUuid , Integer keyVersion , String reason ) {
606705 if (StringUtils .isBlank (hostUuid ) || StringUtils .isBlank (vmUuid ) || keyVersion == null ) {
706+ logger .info (String .format (
707+ "skip delete host secret: reason=%s, hostUuid=%s, vmUuid=%s, keyVersion=%s" ,
708+ reason , hostUuid , vmUuid , keyVersion ));
607709 return ;
608710 }
609711
712+ logger .info (String .format (
713+ "send delete host secret: reason=%s, hostUuid=%s, vmUuid=%s, keyVersion=%s" ,
714+ reason , hostUuid , vmUuid , keyVersion ));
610715 SecretHostDeleteMsg dmsg = new SecretHostDeleteMsg ();
611716 dmsg .setHostUuid (hostUuid );
612717 dmsg .setVmUuid (vmUuid );
0 commit comments