diff --git a/compute/src/main/java/org/zstack/compute/vm/VmNicManagerImpl.java b/compute/src/main/java/org/zstack/compute/vm/VmNicManagerImpl.java index 25c0b005d71..31b3e35d32a 100644 --- a/compute/src/main/java/org/zstack/compute/vm/VmNicManagerImpl.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmNicManagerImpl.java @@ -58,6 +58,10 @@ public void afterAddIpAddress(String vmNicUUid, String usedIpUuid) { SQL.New(UsedIpVO.class).eq(UsedIpVO_.uuid, usedIpUuid).set(UsedIpVO_.vmNicUuid, vmNicUUid).update(); VmNicVO nic = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, vmNicUUid).find(); + if (nic == null) { + logger.debug(String.format("VmNic[uuid:%s] not found, skip afterAddIpAddress", vmNicUUid)); + return; + } UsedIpVO temp = null; /* if there is ipv4 addresses, we put the first attached ipv4 address to VmNic.ip @@ -88,6 +92,10 @@ public void afterAddIpAddress(String vmNicUUid, String usedIpUuid) { @Override public void afterDelIpAddress(String vmNicUUid, String usedIpUuid) { VmNicVO nic = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, vmNicUUid).find(); + if (nic == null) { + logger.debug(String.format("VmNic[uuid:%s] not found, skip afterDelIpAddress", vmNicUUid)); + return; + } if (nic.getUsedIpUuid() != null && !nic.getUsedIpUuid().equals(usedIpUuid)) { return; } diff --git a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json index 32eb4c8f056..715e823d95e 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json @@ -3374,7 +3374,7 @@ "ORG_ZSTACK_NETWORK_HUAWEI_IMASTER_10019": "delete token of SDN controller [IP:%s] failed because %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10004": "Cannot execute volume mapping to host flow due to invalid volume ID.%s", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10007": "port forwarding rule [uuid:%s] has not been attached to any virtual machine network interface, cannot detach", - "ORG_ZSTACK_MEVOCO_10088": "cannot take a snapshot for volumes[%s] when volume[uuid: %s] is not attached", + "ORG_ZSTACK_MEVOCO_10088": "cannot create snapshot for volume[uuid:%s] because it is not attached to any VM instance. Please attach the volume to a VM first. Affected volumes: %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10005": "Cannot execute map LUN to host flow due to invalid LUN type: %s", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10008": "port forwarding rule [uuid:%s] has been associated with vm nic [uuid:%s], cannot be reassigned again", "ORG_ZSTACK_MEVOCO_10087": "A Running VM[uuid:%s] has no associated Host UUID.", diff --git a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json index 84609838ddc..01960e8eb45 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json @@ -3374,7 +3374,7 @@ "ORG_ZSTACK_NETWORK_HUAWEI_IMASTER_10019": "删除 SDN 控制器 [IP:%s] 的令牌失败,因为 %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10004": "无法执行映射LUN到主机流程,无效的LUN ID", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10007": "端口转发规则 rule[uuid:%s] 没有绑定到任何 VM 的网卡上,无法解除绑定", - "ORG_ZSTACK_MEVOCO_10088": "无法为挂载状态以外的卷[%s]创建快照", + "ORG_ZSTACK_MEVOCO_10088": "无法为云盘[uuid:%s]创建快照,因为该云盘未挂载到任何云主机。请先将云盘挂载到云主机后再创建快照。相关云盘: %s", "ORG_ZSTACK_STORAGE_PRIMARY_BLOCK_10005": "无法执行映射LUN到主机流程,无效的LUN类型", "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10008": "端口转发规则[uuid:%s]已绑定到VM网卡[uuid:%s],无法再次绑定", "ORG_ZSTACK_MEVOCO_10087": "如何一个运行中的VM[uuid:%s]没有宿主机uuid?", diff --git a/core/src/main/java/org/zstack/core/cloudbus/ResourceDestinationMakerImpl.java b/core/src/main/java/org/zstack/core/cloudbus/ResourceDestinationMakerImpl.java index 08a776f1db2..9ead578395d 100755 --- a/core/src/main/java/org/zstack/core/cloudbus/ResourceDestinationMakerImpl.java +++ b/core/src/main/java/org/zstack/core/cloudbus/ResourceDestinationMakerImpl.java @@ -27,27 +27,27 @@ public class ResourceDestinationMakerImpl implements ManagementNodeChangeListene private DatabaseFacade dbf; @Override - public void nodeJoin(ManagementNodeInventory inv) { + public synchronized void nodeJoin(ManagementNodeInventory inv) { nodeHash.add(inv.getUuid()); nodes.put(inv.getUuid(), new NodeInfo(inv)); } @Override - public void nodeLeft(ManagementNodeInventory inv) { + public synchronized void nodeLeft(ManagementNodeInventory inv) { String nodeId = inv.getUuid(); nodeHash.remove(nodeId); nodes.remove(nodeId); } @Override - public void iAmDead(ManagementNodeInventory inv) { + public synchronized void iAmDead(ManagementNodeInventory inv) { String nodeId = inv.getUuid(); nodeHash.remove(nodeId); nodes.remove(nodeId); } @Override - public void iJoin(ManagementNodeInventory inv) { + public synchronized void iJoin(ManagementNodeInventory inv) { List lst = Q.New(ManagementNodeVO.class).list(); lst.forEach((ManagementNodeVO node) -> { nodeHash.add(node.getUuid()); @@ -56,7 +56,7 @@ public void iJoin(ManagementNodeInventory inv) { } @Override - public String makeDestination(String resourceUuid) { + public synchronized String makeDestination(String resourceUuid) { String nodeUuid = nodeHash.get(resourceUuid); if (nodeUuid == null) { throw new CloudRuntimeException("Cannot find any available management node to send message"); @@ -66,18 +66,18 @@ public String makeDestination(String resourceUuid) { } @Override - public boolean isManagedByUs(String resourceUuid) { + public synchronized boolean isManagedByUs(String resourceUuid) { String nodeUuid = makeDestination(resourceUuid); return nodeUuid.equals(Platform.getManagementServerId()); } @Override - public Collection getManagementNodesInHashRing() { - return nodeHash.getNodes(); + public synchronized Collection getManagementNodesInHashRing() { + return new ArrayList<>(nodeHash.getNodes()); } @Override - public NodeInfo getNodeInfo(String nodeUuid) { + public synchronized NodeInfo getNodeInfo(String nodeUuid) { NodeInfo info = nodes.get(nodeUuid); if (info == null) { ManagementNodeVO vo = dbf.findByUuid(nodeUuid, ManagementNodeVO.class); @@ -93,17 +93,17 @@ public NodeInfo getNodeInfo(String nodeUuid) { } @Override - public Collection getAllNodeInfo() { - return nodes.values(); + public synchronized Collection getAllNodeInfo() { + return new ArrayList<>(nodes.values()); } @Override - public int getManagementNodeCount() { - return nodes.values().size(); + public synchronized int getManagementNodeCount() { + return nodes.size(); } - public boolean isNodeInCircle(String nodeId) { + public synchronized boolean isNodeInCircle(String nodeId) { return nodeHash.hasNode(nodeId); } } diff --git a/core/src/main/java/org/zstack/core/thread/DispatchQueueImpl.java b/core/src/main/java/org/zstack/core/thread/DispatchQueueImpl.java index e298cdd787f..961b192ac7c 100755 --- a/core/src/main/java/org/zstack/core/thread/DispatchQueueImpl.java +++ b/core/src/main/java/org/zstack/core/thread/DispatchQueueImpl.java @@ -302,7 +302,7 @@ private class SyncTaskFuture extends AbstractFuture { public SyncTaskFuture(SyncTask task) { super(task); - this.parentContext = Context.current(); + this.parentContext = isTelemetryEnabled() ? Context.current() : null; } private SyncTask getTask() { diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java index 7808c227623..a15ed211307 100644 --- a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java @@ -4,8 +4,10 @@ import org.zstack.header.storage.primary.PrimaryStorageInventory; import org.zstack.utils.gson.JSONObjectUtil; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Inventory(mappingVOClass = ExternalPrimaryStorageVO.class) @@ -59,6 +61,7 @@ public ExternalPrimaryStorageInventory(ExternalPrimaryStorageVO lvo) { super(lvo); identity = lvo.getIdentity(); config = JSONObjectUtil.toObject(lvo.getConfig(), LinkedHashMap.class); + desensitizeConfig(config); addonInfo = JSONObjectUtil.toObject(lvo.getAddonInfo(), LinkedHashMap.class); outputProtocols = lvo.getOutputProtocols().stream().map(PrimaryStorageOutputProtocolRefVO::getOutputProtocol).collect(Collectors.toList()); defaultProtocol = lvo.getDefaultProtocol(); @@ -68,6 +71,35 @@ public static ExternalPrimaryStorageInventory valueOf(ExternalPrimaryStorageVO l return new ExternalPrimaryStorageInventory(lvo); } + private static void desensitizeConfig(Map config) { + if (config == null) return; + desensitizeUrlList(config, "mdsUrls"); + desensitizeUrlList(config, "mdsInfos"); + } + + private static void desensitizeUrlList(Map config, String key) { + Object urls = config.get(key); + if (urls instanceof List) { + List desensitized = new ArrayList<>(); + for (Object url : (List) urls) { + desensitized.add(desensitizeUrl(String.valueOf(url))); + } + config.put(key, desensitized); + } + } + + private static String desensitizeUrl(String url) { + int atIndex = url.lastIndexOf('@'); + if (atIndex > 0) { + int schemeIndex = url.indexOf("://"); + if (schemeIndex >= 0 && schemeIndex < atIndex) { + return url.substring(0, schemeIndex + 3) + "***" + url.substring(atIndex); + } + return "***" + url.substring(atIndex); + } + return url; + } + public String getIdentity() { return identity; } diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/group/MemorySnapshotValidatorExtensionPoint.java b/header/src/main/java/org/zstack/header/storage/snapshot/group/MemorySnapshotValidatorExtensionPoint.java index 0d3698457f7..acdc1653390 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/group/MemorySnapshotValidatorExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/group/MemorySnapshotValidatorExtensionPoint.java @@ -3,7 +3,16 @@ import org.zstack.header.errorcode.ErrorCode; /** - * Created by LiangHanYu on 2022/6/9 18:30 + * 内存快照验证扩展点 + * + *

触发时机:创建内存快照前,验证 VM 是否符合条件

+ *

调用位置:VolumeSnapshotGroupCreator.validate()

+ * + *

使用场景:

+ *

各模块通过此扩展点检查 VM 的外部设备或网络引用 + * 是否与内存快照兼容。

+ * + * @since 5.0.0 */ public interface MemorySnapshotValidatorExtensionPoint { default ErrorCode checkVmWhereMemorySnapshotExistExternalDevices(String VmInstanceUuid) { diff --git a/header/src/main/java/org/zstack/header/vm/DetachNicExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/DetachNicExtensionPoint.java index 8d665918a79..6c4f706bfa2 100644 --- a/header/src/main/java/org/zstack/header/vm/DetachNicExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/DetachNicExtensionPoint.java @@ -3,6 +3,11 @@ import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.network.l3.L3NetworkInventory; +/** + * @deprecated This interface has no implementations and should be removed. + * Use {@link org.zstack.header.vm.extensions.VmInstanceDetachNicExtensionPoint} instead. + */ +@Deprecated public interface DetachNicExtensionPoint { ErrorCode validateDetachNicByDriverTypeAndClusterType(L3NetworkInventory l3, VmInstanceInventory vm); } diff --git a/header/src/main/java/org/zstack/header/vm/ResourceConfigMemorySnapshotExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/ResourceConfigMemorySnapshotExtensionPoint.java index 7f45aa3d96d..dcfef17f851 100644 --- a/header/src/main/java/org/zstack/header/vm/ResourceConfigMemorySnapshotExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/ResourceConfigMemorySnapshotExtensionPoint.java @@ -2,6 +2,18 @@ import java.util.List; +/** + * 内存快照资源配置归档扩展点 + * + *

触发时机:创建内存快照时,归档与 VM 相关的资源配置

+ *

调用位置:MemorySnapshotManager.archiveResourceConfig()

+ * + *

使用场景:

+ *

当 VM 执行内存快照时,各模块通过此扩展点提供需要归档的资源配置, + * 以便恢复快照时可以同时恢复相关配置状态。

+ * + * @since 5.0.0 + */ public interface ResourceConfigMemorySnapshotExtensionPoint { List getNeedToArchiveResourceConfig(String resourceUuid); } diff --git a/header/src/main/java/org/zstack/header/vm/VmAfterAttachL3NetworkExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmAfterAttachL3NetworkExtensionPoint.java index a651040d121..2e794002b16 100755 --- a/header/src/main/java/org/zstack/header/vm/VmAfterAttachL3NetworkExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmAfterAttachL3NetworkExtensionPoint.java @@ -3,8 +3,9 @@ import org.zstack.header.network.l3.L3NetworkInventory; /** - * Created by xing5 on 2016/4/18. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint#afterAttachL3Network} instead. */ +@Deprecated public interface VmAfterAttachL3NetworkExtensionPoint { void vmAfterAttachL3Network(VmInstanceInventory vm, L3NetworkInventory l3); } diff --git a/header/src/main/java/org/zstack/header/vm/VmAfterAttachNicExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmAfterAttachNicExtensionPoint.java index 1e553790aa1..1ed18993d4a 100644 --- a/header/src/main/java/org/zstack/header/vm/VmAfterAttachNicExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmAfterAttachNicExtensionPoint.java @@ -4,8 +4,9 @@ import org.zstack.header.core.NoErrorCompletion; /** - * Created by LiangHanYu on 2022/4/13 13:24 + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint#afterAttachL3Network} instead. */ +@Deprecated public interface VmAfterAttachNicExtensionPoint { void afterAttachNic(String nicUuid, VmInstanceInventory vmInstanceInventory, Completion completion); diff --git a/header/src/main/java/org/zstack/header/vm/VmAfterExpungeExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmAfterExpungeExtensionPoint.java index bf36926e65f..bbb844bfce4 100755 --- a/header/src/main/java/org/zstack/header/vm/VmAfterExpungeExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmAfterExpungeExtensionPoint.java @@ -2,8 +2,9 @@ /** - * Created by Mei Lei on 8/23/16. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmExpungeExtensionPoint#afterExpunge} instead. */ +@Deprecated public interface VmAfterExpungeExtensionPoint { void vmAfterExpunge(VmInstanceInventory inv); } diff --git a/header/src/main/java/org/zstack/header/vm/VmBeforeAttachL3NetworkExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmBeforeAttachL3NetworkExtensionPoint.java index c87a891d2ea..a26a21ec28f 100755 --- a/header/src/main/java/org/zstack/header/vm/VmBeforeAttachL3NetworkExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmBeforeAttachL3NetworkExtensionPoint.java @@ -3,8 +3,9 @@ import org.zstack.header.network.l3.L3NetworkInventory; /** - * Created by xing5 on 2016/4/18. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint#beforeAttachL3Network} instead. */ +@Deprecated public interface VmBeforeAttachL3NetworkExtensionPoint { void vmBeforeAttachL3Network(VmInstanceInventory vm, L3NetworkInventory l3); } diff --git a/header/src/main/java/org/zstack/header/vm/VmBeforeExpungeExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmBeforeExpungeExtensionPoint.java index 97aeeea86cf..32b12d03e68 100644 --- a/header/src/main/java/org/zstack/header/vm/VmBeforeExpungeExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmBeforeExpungeExtensionPoint.java @@ -2,8 +2,9 @@ /** - * Created by Mei Lei on 8/23/16. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmExpungeExtensionPoint#beforeExpunge} instead. */ +@Deprecated public interface VmBeforeExpungeExtensionPoint { void vmBeforeExpunge(VmInstanceInventory inv); } diff --git a/header/src/main/java/org/zstack/header/vm/VmDetachNicExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmDetachNicExtensionPoint.java index af11dc0bc66..48ff3ba23ae 100755 --- a/header/src/main/java/org/zstack/header/vm/VmDetachNicExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmDetachNicExtensionPoint.java @@ -3,8 +3,9 @@ import org.zstack.header.errorcode.ErrorCode; /** - * Created by frank on 7/18/2015. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceDetachNicExtensionPoint} instead. */ +@Deprecated public interface VmDetachNicExtensionPoint { void preDetachNic(VmNicInventory nic); diff --git a/header/src/main/java/org/zstack/header/vm/VmFailToAttachL3NetworkExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmFailToAttachL3NetworkExtensionPoint.java index 27f97f23ca8..86769d21d15 100755 --- a/header/src/main/java/org/zstack/header/vm/VmFailToAttachL3NetworkExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmFailToAttachL3NetworkExtensionPoint.java @@ -4,8 +4,9 @@ import org.zstack.header.network.l3.L3NetworkInventory; /** - * Created by xing5 on 2016/4/18. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint#failedToAttachL3Network} instead. */ +@Deprecated public interface VmFailToAttachL3NetworkExtensionPoint { void vmFailToAttachL3Network(VmInstanceInventory vm, L3NetworkInventory l3, ErrorCode error); } diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceAttachNicExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmInstanceAttachNicExtensionPoint.java index 9139f98508f..716b7d6231f 100644 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceAttachNicExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceAttachNicExtensionPoint.java @@ -1,5 +1,10 @@ package org.zstack.header.vm; +/** + * @deprecated This interface has no implementations and should be removed. + * Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint} instead. + */ +@Deprecated public interface VmInstanceAttachNicExtensionPoint { void afterAttachNicToVm(VmNicInventory nic); } diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceDestroyExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmInstanceDestroyExtensionPoint.java index 9e285d22fe7..f493758fbc8 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceDestroyExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceDestroyExtensionPoint.java @@ -2,6 +2,10 @@ import org.zstack.header.errorcode.ErrorCode; +/** + * @deprecated Use {@link org.zstack.header.vm.extensions.VmDestroyExtensionPoint} instead. + */ +@Deprecated public interface VmInstanceDestroyExtensionPoint { String preDestroyVm(VmInstanceInventory inv); diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java index 7007c592aea..99ee2173b98 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java @@ -847,7 +847,9 @@ public void setBootMode(String bootMode) { public long getRootDiskAllocateSize() { if (rootDiskOffering == null) { - return this.getImageSpec().getInventory().getSize(); + long virtualSize = this.getImageSpec().getInventory().getSize(); + long actualSize = this.getImageSpec().getInventory().getActualSize(); + return Math.max(virtualSize, actualSize); } return rootDiskOffering.getDiskSize(); } diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java index 8a755b52fda..49303e23252 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java @@ -168,6 +168,7 @@ public enum VmInstanceState { new Transaction(VmInstanceStateEvent.destroyed, VmInstanceState.Destroyed), new Transaction(VmInstanceStateEvent.destroying, VmInstanceState.Destroying), new Transaction(VmInstanceStateEvent.running, VmInstanceState.Running), + new Transaction(VmInstanceStateEvent.stopped, VmInstanceState.Stopped), new Transaction(VmInstanceStateEvent.expunging, VmInstanceState.Expunging) ); Destroyed.transactions( diff --git a/header/src/main/java/org/zstack/header/vm/VmJustAfterDeleteFromDbExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmJustAfterDeleteFromDbExtensionPoint.java index e4b11666ebc..56c463a2ddc 100644 --- a/header/src/main/java/org/zstack/header/vm/VmJustAfterDeleteFromDbExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmJustAfterDeleteFromDbExtensionPoint.java @@ -1,8 +1,9 @@ package org.zstack.header.vm; /** - * Created by Qi Le on 2019-08-21 + * @deprecated Use {@link org.zstack.header.vm.extensions.VmDbDeleteContext} with a single-phase extension point instead. */ +@Deprecated public interface VmJustAfterDeleteFromDbExtensionPoint { void vmJustAfterDeleteFromDbExtensionPoint(VmInstanceInventory inv, String accountUuid); } diff --git a/header/src/main/java/org/zstack/header/vm/VmJustBeforeDeleteFromDbExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmJustBeforeDeleteFromDbExtensionPoint.java index 4e8337ac805..584ef5ccc55 100755 --- a/header/src/main/java/org/zstack/header/vm/VmJustBeforeDeleteFromDbExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmJustBeforeDeleteFromDbExtensionPoint.java @@ -1,8 +1,9 @@ package org.zstack.header.vm; /** - * Created by xing5 on 2017/6/26. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmDbDeleteContext} with a single-phase extension point instead. */ +@Deprecated public interface VmJustBeforeDeleteFromDbExtensionPoint { void vmJustBeforeDeleteFromDb(VmInstanceInventory inv); } diff --git a/header/src/main/java/org/zstack/header/vm/VmPreAttachL3NetworkExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/VmPreAttachL3NetworkExtensionPoint.java index 84240ff0b11..bb40daa67e9 100755 --- a/header/src/main/java/org/zstack/header/vm/VmPreAttachL3NetworkExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/vm/VmPreAttachL3NetworkExtensionPoint.java @@ -3,8 +3,9 @@ import org.zstack.header.network.l3.L3NetworkInventory; /** - * Created by xing5 on 2016/4/18. + * @deprecated Use {@link org.zstack.header.vm.extensions.VmInstanceAttachL3NetworkExtensionPoint#preAttachL3Network} instead. */ +@Deprecated public interface VmPreAttachL3NetworkExtensionPoint { void vmPreAttachL3Network(VmInstanceInventory vm, L3NetworkInventory l3); } diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmAttachL3NetworkContext.java b/header/src/main/java/org/zstack/header/vm/extensions/VmAttachL3NetworkContext.java new file mode 100644 index 00000000000..b874a20347e --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmAttachL3NetworkContext.java @@ -0,0 +1,67 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.network.l3.L3NetworkInventory; +import org.zstack.header.vm.VmInstanceInventory; +import org.zstack.header.vm.VmNicInventory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Context object for VM L3 Network attachment operations. + * + *

This context is passed through all lifecycle phases (pre/before/after/failedTo) + * allowing extensions to share state between phases.

+ */ +public class VmAttachL3NetworkContext { + private VmInstanceInventory vm; + private L3NetworkInventory l3; + private VmNicInventory nic; + private String accountUuid; + private Map additionalData = new HashMap<>(); + + public VmInstanceInventory getVm() { + return vm; + } + + public void setVm(VmInstanceInventory vm) { + this.vm = vm; + } + + public L3NetworkInventory getL3() { + return l3; + } + + public void setL3(L3NetworkInventory l3) { + this.l3 = l3; + } + + public VmNicInventory getNic() { + return nic; + } + + public void setNic(VmNicInventory nic) { + this.nic = nic; + } + + public String getAccountUuid() { + return accountUuid; + } + + public void setAccountUuid(String accountUuid) { + this.accountUuid = accountUuid; + } + + public Map getAdditionalData() { + return additionalData; + } + + public void putData(String key, Object value) { + additionalData.put(key, value); + } + + @SuppressWarnings("unchecked") + public T getData(String key) { + return (T) additionalData.get(key); + } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmDbDeleteContext.java b/header/src/main/java/org/zstack/header/vm/extensions/VmDbDeleteContext.java new file mode 100644 index 00000000000..7e484498952 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmDbDeleteContext.java @@ -0,0 +1,21 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.vm.VmInstanceInventory; + +import java.util.HashMap; +import java.util.Map; + +public class VmDbDeleteContext { + private VmInstanceInventory vm; + private String accountUuid; + private Map additionalData = new HashMap<>(); + + public VmInstanceInventory getVm() { return vm; } + public void setVm(VmInstanceInventory vm) { this.vm = vm; } + public String getAccountUuid() { return accountUuid; } + public void setAccountUuid(String accountUuid) { this.accountUuid = accountUuid; } + public Map getAdditionalData() { return additionalData; } + public void putData(String key, Object value) { additionalData.put(key, value); } + @SuppressWarnings("unchecked") + public T getData(String key) { return (T) additionalData.get(key); } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyContext.java b/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyContext.java new file mode 100644 index 00000000000..ae5eb770b02 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyContext.java @@ -0,0 +1,25 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.vm.VmInstanceInventory; + +import java.util.HashMap; +import java.util.Map; + +public class VmDestroyContext { + private VmInstanceInventory vm; + private String accountUuid; + private ErrorCode error; + private Map additionalData = new HashMap<>(); + + public VmInstanceInventory getVm() { return vm; } + public void setVm(VmInstanceInventory vm) { this.vm = vm; } + public String getAccountUuid() { return accountUuid; } + public void setAccountUuid(String accountUuid) { this.accountUuid = accountUuid; } + public ErrorCode getError() { return error; } + public void setError(ErrorCode error) { this.error = error; } + public Map getAdditionalData() { return additionalData; } + public void putData(String key, Object value) { additionalData.put(key, value); } + @SuppressWarnings("unchecked") + public T getData(String key) { return (T) additionalData.get(key); } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyExtensionPoint.java new file mode 100644 index 00000000000..d62a782da5b --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmDestroyExtensionPoint.java @@ -0,0 +1,24 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.core.Completion; + +/** + * VM Instance Destroy Extension Point (unified) + * + *

Trigger: When user calls DestroyVmInstance API

+ *

Call site: VmInstanceBase via VmInstanceExtensionPointEmitter

+ * + *

Phase Description:

+ *
    + *
  • preDestroy - Can reject the destroy operation
  • + *
  • beforeDestroy - Preparation before destroy
  • + *
  • afterDestroy - Post-processing after destroy
  • + *
  • failedToDestroy - Cleanup on failure
  • + *
+ */ +public interface VmDestroyExtensionPoint { + default String preDestroy(VmDestroyContext ctx) { return null; } + default void beforeDestroy(VmDestroyContext ctx) {} + default void afterDestroy(VmDestroyContext ctx, Completion completion) { completion.success(); } + default void failedToDestroy(VmDestroyContext ctx) {} +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmDetachNicContext.java b/header/src/main/java/org/zstack/header/vm/extensions/VmDetachNicContext.java new file mode 100644 index 00000000000..bcf0129bc01 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmDetachNicContext.java @@ -0,0 +1,67 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.vm.VmInstanceInventory; +import org.zstack.header.vm.VmNicInventory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Context object for VM NIC detachment operations. + * + *

This context is passed through all lifecycle phases (pre/before/after/failedTo) + * allowing extensions to share state between phases.

+ */ +public class VmDetachNicContext { + private VmInstanceInventory vm; + private VmNicInventory nic; + private String accountUuid; + private ErrorCode error; + private Map additionalData = new HashMap<>(); + + public VmInstanceInventory getVm() { + return vm; + } + + public void setVm(VmInstanceInventory vm) { + this.vm = vm; + } + + public VmNicInventory getNic() { + return nic; + } + + public void setNic(VmNicInventory nic) { + this.nic = nic; + } + + public String getAccountUuid() { + return accountUuid; + } + + public void setAccountUuid(String accountUuid) { + this.accountUuid = accountUuid; + } + + public ErrorCode getError() { + return error; + } + + public void setError(ErrorCode error) { + this.error = error; + } + + public Map getAdditionalData() { + return additionalData; + } + + public void putData(String key, Object value) { + additionalData.put(key, value); + } + + @SuppressWarnings("unchecked") + public T getData(String key) { + return (T) additionalData.get(key); + } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeContext.java b/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeContext.java new file mode 100644 index 00000000000..e5c7d21f807 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeContext.java @@ -0,0 +1,25 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.vm.VmInstanceInventory; + +import java.util.HashMap; +import java.util.Map; + +public class VmExpungeContext { + private VmInstanceInventory vm; + private String accountUuid; + private ErrorCode error; + private Map additionalData = new HashMap<>(); + + public VmInstanceInventory getVm() { return vm; } + public void setVm(VmInstanceInventory vm) { this.vm = vm; } + public String getAccountUuid() { return accountUuid; } + public void setAccountUuid(String accountUuid) { this.accountUuid = accountUuid; } + public ErrorCode getError() { return error; } + public void setError(ErrorCode error) { this.error = error; } + public Map getAdditionalData() { return additionalData; } + public void putData(String key, Object value) { additionalData.put(key, value); } + @SuppressWarnings("unchecked") + public T getData(String key) { return (T) additionalData.get(key); } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeExtensionPoint.java new file mode 100644 index 00000000000..7b7dd9e582d --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmExpungeExtensionPoint.java @@ -0,0 +1,24 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.core.Completion; + +/** + * VM Instance Expunge Extension Point (unified) + * + *

Trigger: When VM is expunged (after destroy + grace period)

+ *

Call site: VmInstanceBase.expunge()

+ * + *

Phase Description:

+ *
    + *
  • preExpunge - Can reject the expunge operation
  • + *
  • beforeExpunge - Preparation before expunge
  • + *
  • afterExpunge - Post-processing after expunge
  • + *
  • failedToExpunge - Cleanup on failure
  • + *
+ */ +public interface VmExpungeExtensionPoint { + default String preExpunge(VmExpungeContext ctx) { return null; } + default void beforeExpunge(VmExpungeContext ctx) {} + default void afterExpunge(VmExpungeContext ctx, Completion completion) { completion.success(); } + default void failedToExpunge(VmExpungeContext ctx) {} +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceAttachL3NetworkExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceAttachL3NetworkExtensionPoint.java new file mode 100644 index 00000000000..fc354fb8717 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceAttachL3NetworkExtensionPoint.java @@ -0,0 +1,63 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.core.Completion; + +/** + * VM L3 Network Attachment Extension Point + * + *

Trigger: When user calls AttachL3NetworkToVm API

+ *

Call site: VmInstanceBase.attachL3Network()

+ * + *

Phase Description:

+ *
    + *
  • preAttachL3Network - Before NIC creation, can reject the attachment
  • + *
  • beforeAttachL3Network - NIC prepared, about to execute FlowChain
  • + *
  • afterAttachL3Network - Attachment complete, network services configured
  • + *
  • failedToAttachL3Network - Attachment failed, cleanup resources
  • + *
+ * + *

Method Signatures:

+ *
    + *
  • pre: Returns error message String, null means pass
  • + *
  • before: Synchronous void, for preparation
  • + *
  • after: Asynchronous with Completion, for post-processing
  • + *
  • failedTo: Synchronous void, for cleanup
  • + *
+ */ +public interface VmInstanceAttachL3NetworkExtensionPoint { + /** + * Called before NIC creation. Can reject the attachment by returning an error message. + * + * @param ctx the attachment context + * @return error message if rejected, null if passed + */ + default String preAttachL3Network(VmAttachL3NetworkContext ctx) { + return null; + } + + /** + * Called after NIC is prepared, before executing the attachment FlowChain. + * + * @param ctx the attachment context + */ + default void beforeAttachL3Network(VmAttachL3NetworkContext ctx) { + } + + /** + * Called after attachment is complete. Asynchronous for post-processing. + * + * @param ctx the attachment context (nic field is populated) + * @param completion callback to signal completion + */ + default void afterAttachL3Network(VmAttachL3NetworkContext ctx, Completion completion) { + completion.success(); + } + + /** + * Called when attachment fails. Cleanup any resources allocated in previous phases. + * + * @param ctx the attachment context + */ + default void failedToAttachL3Network(VmAttachL3NetworkContext ctx) { + } +} diff --git a/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceDetachNicExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceDetachNicExtensionPoint.java new file mode 100644 index 00000000000..57aea614821 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/extensions/VmInstanceDetachNicExtensionPoint.java @@ -0,0 +1,63 @@ +package org.zstack.header.vm.extensions; + +import org.zstack.header.core.Completion; + +/** + * VM NIC Detachment Extension Point + * + *

Trigger: When user calls DetachL3NetworkFromVm API

+ *

Call site: VmInstanceBase.detachNic()

+ * + *

Phase Description:

+ *
    + *
  • preDetachNic - Before detachment, can reject the operation
  • + *
  • beforeDetachNic - About to execute detachment FlowChain
  • + *
  • afterDetachNic - Detachment complete, NIC removed
  • + *
  • failedToDetachNic - Detachment failed, restore state
  • + *
+ * + *

Method Signatures:

+ *
    + *
  • pre: Returns error message String, null means pass
  • + *
  • before: Synchronous void, for preparation
  • + *
  • after: Asynchronous with Completion, for post-processing
  • + *
  • failedTo: Synchronous void, for cleanup
  • + *
+ */ +public interface VmInstanceDetachNicExtensionPoint { + /** + * Called before NIC detachment. Can reject the operation by returning an error message. + * + * @param ctx the detachment context + * @return error message if rejected, null if passed + */ + default String preDetachNic(VmDetachNicContext ctx) { + return null; + } + + /** + * Called before executing the detachment FlowChain. + * + * @param ctx the detachment context + */ + default void beforeDetachNic(VmDetachNicContext ctx) { + } + + /** + * Called after detachment is complete. Asynchronous for post-processing. + * + * @param ctx the detachment context + * @param completion callback to signal completion + */ + default void afterDetachNic(VmDetachNicContext ctx, Completion completion) { + completion.success(); + } + + /** + * Called when detachment fails. Restore any state changed in previous phases. + * + * @param ctx the detachment context (error field is populated) + */ + default void failedToDetachNic(VmDetachNicContext ctx) { + } +} diff --git a/header/src/main/java/org/zstack/header/volume/VolumeBeforeExpungeExtensionPoint.java b/header/src/main/java/org/zstack/header/volume/VolumeBeforeExpungeExtensionPoint.java index 673c7cbf9bd..ba4594264ac 100755 --- a/header/src/main/java/org/zstack/header/volume/VolumeBeforeExpungeExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/volume/VolumeBeforeExpungeExtensionPoint.java @@ -4,8 +4,9 @@ import org.zstack.header.core.NoErrorCompletion; /** - * Created by xing5 on 2016/5/3. + * @deprecated Use {@link org.zstack.header.volume.extensions.VolumeExpungeExtPoint} instead. */ +@Deprecated public interface VolumeBeforeExpungeExtensionPoint { void volumePreExpunge(VolumeInventory volume); void volumeBeforeExpunge(VolumeInventory volume, NoErrorCompletion completion); diff --git a/header/src/main/java/org/zstack/header/volume/VolumeDeletionExtensionPoint.java b/header/src/main/java/org/zstack/header/volume/VolumeDeletionExtensionPoint.java index 8985e000c3f..3b40566e230 100755 --- a/header/src/main/java/org/zstack/header/volume/VolumeDeletionExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/volume/VolumeDeletionExtensionPoint.java @@ -4,8 +4,9 @@ import org.zstack.header.errorcode.ErrorCode; /** - * Created by frank on 6/18/2015. + * @deprecated Use {@link org.zstack.header.volume.extensions.VolumeDeletionExtPoint} instead. */ +@Deprecated public interface VolumeDeletionExtensionPoint { void preDeleteVolume(VolumeInventory volume); diff --git a/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeleteContext.java b/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeleteContext.java new file mode 100644 index 00000000000..7c3efdec410 --- /dev/null +++ b/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeleteContext.java @@ -0,0 +1,25 @@ +package org.zstack.header.volume.extensions; + +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.volume.VolumeInventory; + +import java.util.HashMap; +import java.util.Map; + +public class VolumeDeleteContext { + private VolumeInventory volume; + private String accountUuid; + private ErrorCode error; + private Map additionalData = new HashMap<>(); + + public VolumeInventory getVolume() { return volume; } + public void setVolume(VolumeInventory volume) { this.volume = volume; } + public String getAccountUuid() { return accountUuid; } + public void setAccountUuid(String accountUuid) { this.accountUuid = accountUuid; } + public ErrorCode getError() { return error; } + public void setError(ErrorCode error) { this.error = error; } + public Map getAdditionalData() { return additionalData; } + public void putData(String key, Object value) { additionalData.put(key, value); } + @SuppressWarnings("unchecked") + public T getData(String key) { return (T) additionalData.get(key); } +} diff --git a/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeletionExtPoint.java b/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeletionExtPoint.java new file mode 100644 index 00000000000..f365747c6a8 --- /dev/null +++ b/header/src/main/java/org/zstack/header/volume/extensions/VolumeDeletionExtPoint.java @@ -0,0 +1,24 @@ +package org.zstack.header.volume.extensions; + +import org.zstack.header.core.Completion; + +/** + * Volume Deletion Extension Point (unified) + * + *

Trigger: When user calls DeleteDataVolume API

+ *

Call site: VolumeBase.delete()

+ * + *

Phase Description:

+ *
    + *
  • preDelete - Can reject the deletion
  • + *
  • beforeDelete - Preparation before deletion
  • + *
  • afterDelete - Post-processing after deletion
  • + *
  • failedToDelete - Cleanup on failure
  • + *
+ */ +public interface VolumeDeletionExtPoint { + default String preDelete(VolumeDeleteContext ctx) { return null; } + default void beforeDelete(VolumeDeleteContext ctx) {} + default void afterDelete(VolumeDeleteContext ctx, Completion completion) { completion.success(); } + default void failedToDelete(VolumeDeleteContext ctx) {} +} diff --git a/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeContext.java b/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeContext.java new file mode 100644 index 00000000000..6488058d348 --- /dev/null +++ b/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeContext.java @@ -0,0 +1,25 @@ +package org.zstack.header.volume.extensions; + +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.volume.VolumeInventory; + +import java.util.HashMap; +import java.util.Map; + +public class VolumeExpungeContext { + private VolumeInventory volume; + private String accountUuid; + private ErrorCode error; + private Map additionalData = new HashMap<>(); + + public VolumeInventory getVolume() { return volume; } + public void setVolume(VolumeInventory volume) { this.volume = volume; } + public String getAccountUuid() { return accountUuid; } + public void setAccountUuid(String accountUuid) { this.accountUuid = accountUuid; } + public ErrorCode getError() { return error; } + public void setError(ErrorCode error) { this.error = error; } + public Map getAdditionalData() { return additionalData; } + public void putData(String key, Object value) { additionalData.put(key, value); } + @SuppressWarnings("unchecked") + public T getData(String key) { return (T) additionalData.get(key); } +} diff --git a/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeExtPoint.java b/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeExtPoint.java new file mode 100644 index 00000000000..b51dd4c5c15 --- /dev/null +++ b/header/src/main/java/org/zstack/header/volume/extensions/VolumeExpungeExtPoint.java @@ -0,0 +1,24 @@ +package org.zstack.header.volume.extensions; + +import org.zstack.header.core.Completion; + +/** + * Volume Expunge Extension Point (unified) + * + *

Trigger: When volume is expunged (after delete + grace period)

+ *

Call site: VolumeBase.expunge()

+ * + *

Phase Description:

+ *
    + *
  • preExpunge - Can reject the expunge (or skip it)
  • + *
  • beforeExpunge - Preparation before expunge
  • + *
  • afterExpunge - Post-processing after expunge
  • + *
  • failedToExpunge - Cleanup on failure
  • + *
+ */ +public interface VolumeExpungeExtPoint { + default String preExpunge(VolumeExpungeContext ctx) { return null; } + default void beforeExpunge(VolumeExpungeContext ctx) {} + default void afterExpunge(VolumeExpungeContext ctx, Completion completion) { completion.success(); } + default void failedToExpunge(VolumeExpungeContext ctx) {} +} diff --git a/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java b/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java index 5536a5fc487..b1b0b92d497 100755 --- a/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java +++ b/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java @@ -1075,6 +1075,13 @@ private void handle(APIGetFreeIpMsg msg) { } limit -= freeIpInventorys.size(); } + + Set reservedIpRanges = self.getReservedIpRanges(); + if (reservedIpRanges != null && !reservedIpRanges.isEmpty()) { + freeIpInventorys.removeIf(freeIp -> reservedIpRanges.stream().anyMatch( + r -> NetworkUtils.isInRange(freeIp.getIp(), r.getStartIp(), r.getEndIp()))); + } + reply.setInventories(freeIpInventorys); bus.reply(msg, reply); diff --git a/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java b/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java index d80b40a1d6a..8b387306683 100755 --- a/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java +++ b/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java @@ -5446,7 +5446,7 @@ private void deleteSnapshotOnPrimaryStorage(final DeleteSnapshotOnPrimaryStorage httpCall(DELETE_SNAPSHOT_PATH, cmd, DeleteSnapshotRsp.class, new ReturnValueCompletion(msg) { @Override public void success(DeleteSnapshotRsp returnValue) { - osdHelper.releaseAvailableCapacity(msg.getSnapshot().getPrimaryStorageInstallPath(), msg.getSnapshot().getSize()); + osdHelper.releaseAvailableCapWithRatio(msg.getSnapshot().getPrimaryStorageInstallPath(), msg.getSnapshot().getSize()); bus.reply(msg, reply); completion.done(); } diff --git a/plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java b/plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java index 97b88c919c2..0d9946d5320 100755 --- a/plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java +++ b/plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java @@ -39,6 +39,7 @@ import org.zstack.network.service.vip.VipVO_; import org.zstack.tag.PatternedSystemTag; import org.zstack.tag.TagManager; +import org.zstack.core.upgrade.UpgradeGlobalConfig; import org.zstack.utils.*; import org.zstack.utils.function.ForEachFunction; import org.zstack.utils.logging.CLogger; @@ -152,10 +153,22 @@ public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionExcepti validate((APIGetCandidateVmNicsForLoadBalancerServerGroupMsg)msg); } else if (msg instanceof APIChangeLoadBalancerBackendServerMsg) { validate((APIChangeLoadBalancerBackendServerMsg)msg); + } else if (msg instanceof APIDeleteLoadBalancerMsg) { + validate((APIDeleteLoadBalancerMsg) msg); } return msg; } + private void validate(APIDeleteLoadBalancerMsg msg) { + if (UpgradeGlobalConfig.GRAYSCALE_UPGRADE.value(Boolean.class)) { + LoadBalancerVO lb = dbf.findByUuid(msg.getUuid(), LoadBalancerVO.class); + if (lb != null && lb.getType() == LoadBalancerType.SLB) { + throw new ApiMessageInterceptionException(argerr( + "cannot delete the standalone load balancer[uuid:%s] during grayscale upgrade", msg.getUuid())); + } + } + } + private void validate(APIDeleteAccessControlListMsg msg) { /*List refs = Q.New(LoadBalancerListenerACLRefVO.class).select(LoadBalancerListenerACLRefVO_.listenerUuid) .eq(LoadBalancerListenerACLRefVO_.aclUuid, msg.getUuid()).isNull(LoadBalancerListenerACLRefVO_.serverGroupUuid).listValues(); diff --git a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java index db06239acb3..276ab367ba1 100644 --- a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java +++ b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java @@ -179,7 +179,10 @@ public List getActiveClients(String installPath, String prot if (VolumeProtocol.CBD.toString().equals(protocol)) { GetVolumeClientsCmd cmd = new GetVolumeClientsCmd(); cmd.setPath(installPath); - GetVolumeClientsRsp rsp = syncHttpCall(GET_VOLUME_CLIENTS_PATH, cmd, GetVolumeClientsRsp.class); + GetVolumeClientsRsp rsp = new HttpCaller<>(GET_VOLUME_CLIENTS_PATH, cmd, GetVolumeClientsRsp.class, + null, TimeUnit.SECONDS, 30, true) + .setTryNext(true) + .syncCall(); List clients = new ArrayList<>(); if (!rsp.isSuccess()) { @@ -1411,6 +1414,11 @@ public class HttpCaller { private boolean tryNext = false; + HttpCaller setTryNext(boolean tryNext) { + this.tryNext = tryNext; + return this; + } + public HttpCaller(String path, AgentCommand cmd, Class retClass, ReturnValueCompletion callback) { this(path, cmd, retClass, callback, null, 0, false); } diff --git a/portal/src/main/java/org/zstack/portal/managementnode/ManagementNodeManagerImpl.java b/portal/src/main/java/org/zstack/portal/managementnode/ManagementNodeManagerImpl.java index a945ab77274..4ece718ff52 100755 --- a/portal/src/main/java/org/zstack/portal/managementnode/ManagementNodeManagerImpl.java +++ b/portal/src/main/java/org/zstack/portal/managementnode/ManagementNodeManagerImpl.java @@ -74,6 +74,7 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -107,6 +108,15 @@ public class ManagementNodeManagerImpl extends AbstractService implements Manage // A dictionary (nodeId -> ManagementNodeInventory) of joined management Node final private Map joinedManagementNodes = new ConcurrentHashMap<>(); + // Lock to serialize lifecycle events from heartbeat reconciliation and canonical event callbacks, + // preventing race conditions where a nodeJoin event is immediately followed by a stale nodeLeft + // from the heartbeat thread, or vice versa. See ZSTAC-77711. + private final Object lifecycleLock = new Object(); + + // Track nodes found in hash ring but missing from DB. Only call nodeLeft after a node + // is missing for two consecutive heartbeat cycles, to avoid removing nodes that just joined. + private final Set suspectedMissingFromDb = new HashSet<>(); + private static int NODE_STARTING = 0; private static int NODE_RUNNING = 1; private static int NODE_FAILED = -1; @@ -368,12 +378,16 @@ protected void run(Map tokens, Object data) { ManagementNodeLifeCycleData d = (ManagementNodeLifeCycleData) data; - if (LifeCycle.NodeJoin.toString().equals(d.getLifeCycle())) { - nodeLifeCycle.nodeJoin(d.getInventory()); - } else if (LifeCycle.NodeLeft.toString().equals(d.getLifeCycle())) { - nodeLifeCycle.nodeLeft(d.getInventory()); - } else { - throw new CloudRuntimeException(String.format("unknown lifecycle[%s]", d.getLifeCycle())); + synchronized (lifecycleLock) { + if (LifeCycle.NodeJoin.toString().equals(d.getLifeCycle())) { + // Clear from suspected set since the node is confirmed alive + suspectedMissingFromDb.remove(d.getInventory().getUuid()); + nodeLifeCycle.nodeJoin(d.getInventory()); + } else if (LifeCycle.NodeLeft.toString().equals(d.getLifeCycle())) { + nodeLifeCycle.nodeLeft(d.getInventory()); + } else { + throw new CloudRuntimeException(String.format("unknown lifecycle[%s]", d.getLifeCycle())); + } } } }; @@ -860,34 +874,55 @@ private void checkAllNodesHealth() { Set nodeUuidsInDb = nodesInDb.stream().map(ManagementNodeVO::getUuid).collect(Collectors.toSet()); - // When a node is dying, we may not receive the the dead notification because the message bus may be also dead - // at that moment. By checking if the node UUID is still in our hash ring, we know what nodes should be kicked out - destinationMaker.getManagementNodesInHashRing().forEach(nodeUuid -> { - if (!nodeUuidsInDb.contains(nodeUuid)) { - logger.warn(String.format("found that a management node[uuid:%s] had no heartbeat in database but still in our hash ring," + - "notify that it's dead", nodeUuid)); - ManagementNodeInventory inv = new ManagementNodeInventory(); - inv.setUuid(nodeUuid); - inv.setHostName(destinationMaker.getNodeInfo(nodeUuid).getNodeIP()); - - nodeLifeCycle.nodeLeft(inv); - } - }); - - // check if any node missing in our hash ring - nodesInDb.forEach(n -> { - if (n.getUuid().equals(node().getUuid()) || suspects.contains(n)) { - return; - } - - new Runnable() { - @Override - @AsyncThread - public void run() { - nodeLifeCycle.nodeJoin(ManagementNodeInventory.valueOf(n)); + // Reconcile hash ring with DB under lifecycleLock to prevent race with + // canonical event callbacks (nodeJoin/nodeLeft). See ZSTAC-77711. + synchronized (lifecycleLock) { + // When a node is dying, we may not receive the dead notification because the message bus may be also dead + // at that moment. By checking if the node UUID is still in our hash ring, we know what nodes should be kicked out. + // Use two-round confirmation: first round marks as suspected, second round actually removes. + Set currentSuspected = new HashSet<>(); + destinationMaker.getManagementNodesInHashRing().forEach(nodeUuid -> { + if (!nodeUuidsInDb.contains(nodeUuid)) { + if (suspectedMissingFromDb.contains(nodeUuid)) { + // Second consecutive detection — confirmed missing, remove from hash ring + logger.warn(String.format("management node[uuid:%s] confirmed missing from database for two consecutive" + + " heartbeat cycles, removing from hash ring", nodeUuid)); + ManagementNodeInventory inv = new ManagementNodeInventory(); + inv.setUuid(nodeUuid); + try { + inv.setHostName(destinationMaker.getNodeInfo(nodeUuid).getNodeIP()); + } catch (Exception e) { + logger.warn(String.format("cannot get node info for node[uuid:%s], use empty hostname", nodeUuid)); + } + + nodeLifeCycle.nodeLeft(inv); + } else { + // First detection — mark as suspected, defer removal to next cycle + logger.warn(String.format("management node[uuid:%s] not found in database but still in hash ring," + + " marking as suspected (will remove on next heartbeat if still missing)", nodeUuid)); + currentSuspected.add(nodeUuid); + } } - }.run(); - }); + }); + // Update suspected set: only keep nodes that are newly suspected this round + suspectedMissingFromDb.clear(); + suspectedMissingFromDb.addAll(currentSuspected); + + // check if any node missing in our hash ring + nodesInDb.forEach(n -> { + if (n.getUuid().equals(node().getUuid()) || suspects.contains(n)) { + return; + } + + new Runnable() { + @Override + @AsyncThread + public void run() { + nodeLifeCycle.nodeJoin(ManagementNodeInventory.valueOf(n)); + } + }.run(); + }); + } } @Override diff --git a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java index a0f09d4f1e9..a550fb7d673 100644 --- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java +++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java @@ -6274,6 +6274,8 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_STORAGE_BACKUP_10133 = "ORG_ZSTACK_STORAGE_BACKUP_10133"; + public static final String ORG_ZSTACK_STORAGE_BACKUP_CANCEL_TIMEOUT = "ORG_ZSTACK_STORAGE_BACKUP_CANCEL_TIMEOUT"; + public static final String ORG_ZSTACK_COMPUTE_10000 = "ORG_ZSTACK_COMPUTE_10000"; public static final String ORG_ZSTACK_COMPUTE_10001 = "ORG_ZSTACK_COMPUTE_10001";