From 70eee8c2024f2b3585824ec15d362e86036d1e4d Mon Sep 17 00:00:00 2001 From: "ye.zou" Date: Fri, 13 Feb 2026 11:14:44 +0800 Subject: [PATCH 1/2] [sdnController]: validate L2 network detach before SDN controller delete Resolves: ZSTAC-80186 Change-Id: I8dcb689b022ad907c12bc1b481fb9d0db1e98d06 --- .../sdnController/SdnControllerApiInterceptor.java | 13 +++++++++++++ .../clouderrorcode/CloudOperationsErrorCode.java | 2 ++ 2 files changed, 15 insertions(+) diff --git a/plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java b/plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java index 97840150a1..fa0f9c1932 100644 --- a/plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java +++ b/plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java @@ -87,6 +87,8 @@ public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionExcepti validate((APIPullSdnControllerTenantMsg) msg); } else if (msg instanceof APIChangeSdnControllerMsg) { validate((APIChangeSdnControllerMsg) msg); + } else if (msg instanceof APIRemoveSdnControllerMsg) { + validate((APIRemoveSdnControllerMsg) msg); } setServiceId(msg); @@ -311,6 +313,17 @@ private boolean isOverlappedVlanRange(int start, int end, Integer startVlan, Int (start <= startVlan && end >= endVlan); } + private void validate(APIRemoveSdnControllerMsg msg) { + long poolCount = Q.New(HardwareL2VxlanNetworkPoolVO.class) + .eq(HardwareL2VxlanNetworkPoolVO_.sdnControllerUuid, msg.getUuid()) + .count(); + if (poolCount > 0) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_10031, + "could not remove sdn controller[uuid:%s] because it still has %d L2 vxlan network pool(s) attached. Please detach them first", + msg.getUuid(), poolCount)); + } + } + private void validate(APIChangeSdnControllerMsg msg) { if (msg.getVlanRanges() != null && !msg.getVlanRanges().isEmpty()) { validateVlanRanges(msg.getVlanRanges()); 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 a0f09d4f1e..f3a60d4978 100644 --- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java +++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java @@ -15264,6 +15264,8 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_SDNCONTROLLER_10030 = "ORG_ZSTACK_SDNCONTROLLER_10030"; + public static final String ORG_ZSTACK_SDNCONTROLLER_10031 = "ORG_ZSTACK_SDNCONTROLLER_10031"; + public static final String ORG_ZSTACK_TEST_INTEGRATION_PREMIUM_ZSV_SNAPSHOT_10000 = "ORG_ZSTACK_TEST_INTEGRATION_PREMIUM_ZSV_SNAPSHOT_10000"; public static final String ORG_ZSTACK_TEST_INTEGRATION_PREMIUM_ZSV_SNAPSHOT_10001 = "ORG_ZSTACK_TEST_INTEGRATION_PREMIUM_ZSV_SNAPSHOT_10001"; From 72b873f469110377ced11de7d4c58a760637ed0e Mon Sep 17 00:00:00 2001 From: "ye.zou" Date: Mon, 16 Feb 2026 15:33:07 +0800 Subject: [PATCH 2/2] [sdnController]: delete pools before SDN controller in test cleanup Resolves: ZSTAC-80186 Change-Id: Ief74f07715f75fe86ae932a8a0cffc91e4bc36c1 --- .../network/sdnController/HardwareVxlanCase.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/src/test/groovy/org/zstack/test/integration/network/sdnController/HardwareVxlanCase.groovy b/test/src/test/groovy/org/zstack/test/integration/network/sdnController/HardwareVxlanCase.groovy index d5f0c01eb1..1cebbf6802 100644 --- a/test/src/test/groovy/org/zstack/test/integration/network/sdnController/HardwareVxlanCase.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/network/sdnController/HardwareVxlanCase.groovy @@ -41,6 +41,11 @@ class HardwareVxlanCase extends SubCase { @Override void clean() { + // ZSTAC-80186: delete pools created in createEnv() before env.delete() + // removes the SDN controller (which now validates no attached pools) + queryL2VxlanNetworkPool {}.each { pool -> + deleteL2Network { uuid = pool.uuid } + } env.delete() }