e, EnvSpec spec ->
+ // Build volume-client mappings from volume-client-group mappings and iscsi clients
+ def mappingItems = []
+ int mappingIdSeq = 0
+ volumeClientGroupMappings.values().each { vcgMapping ->
+ int volId = vcgMapping.spec.bs_volume_id
+ int groupId = vcgMapping.spec.iscsi_client_group_id
+ // Find clients in this group
+ iscsiClients.values().each { client ->
+ if (client.spec.iscsi_client_group_id == groupId) {
+ mappingIdSeq++
+ mappingItems.add([
+ metadata: [id: mappingIdSeq, name: "vol-client-mapping-${mappingIdSeq}".toString(), state: [state: "active"]],
+ spec: [
+ id: mappingIdSeq, bs_volume_id: volId,
+ iscsi_client_id: client.spec.id,
+ iscsi_client_group_id: groupId,
+ protocol: "iSCSI", lun_id: vcgMapping.spec.lun_id
+ ],
+ status: [id: mappingIdSeq]
+ ])
+ }
+ }
+ }
+
+ def qParam = req.getParameter("q")
+ def filtered = filterItems(mappingItems, qParam)
+ return makeQueryResponse(filtered)
+ }
+ }
+ }
+
@Override
SpecID create(String uuid, String sessionId) {
inventory = addExternalPrimaryStorage {
diff --git a/testlib/src/main/java/org/zstack/testlib/SpringSpec.groovy b/testlib/src/main/java/org/zstack/testlib/SpringSpec.groovy
index 6346b1cc68d..a8e6812c992 100755
--- a/testlib/src/main/java/org/zstack/testlib/SpringSpec.groovy
+++ b/testlib/src/main/java/org/zstack/testlib/SpringSpec.groovy
@@ -105,6 +105,10 @@ class SpringSpec {
include("iscsi.xml")
}
+ void xinfini() {
+ include("xinfini.xml")
+ }
+
void zbs() {
include("zbs.xml")
include("cbd.xml")
diff --git a/testlib/src/main/java/org/zstack/testlib/Test.groovy b/testlib/src/main/java/org/zstack/testlib/Test.groovy
index 1dcb02c54e9..40f46defad1 100755
--- a/testlib/src/main/java/org/zstack/testlib/Test.groovy
+++ b/testlib/src/main/java/org/zstack/testlib/Test.groovy
@@ -120,6 +120,8 @@ abstract class Test extends ApiHelper implements Retry {
lb()
nfsPrimaryStorage()
externalPrimaryStorage()
+ expon()
+ xinfini()
zbs()
eip()
portForwarding()
@@ -999,6 +1001,20 @@ mysqldump -u root zstack > ${failureLogDir.absolutePath}/dbdump.sql
}
}
+ /**
+ * Expect an API call to fail and verify the error details.
+ * The second closure's delegate is the parsed {@link ErrorCodeList}, so you can
+ * directly access {@code code}, {@code details}, {@code globalErrorCode}, etc.
+ *
+ * Example:
+ *
+ * expectApiFailure {
+ * someApiCall { ... }
+ * } {
+ * assert details.contains("expected error keyword")
+ * }
+ *
+ */
static void expectApiFailure(Closure c, @DelegatesTo(strategy = Closure.OWNER_FIRST, value = ErrorCodeList.class) Closure errorCodeChecker) {
AssertionError error = null
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..ac2eeb7fd8f 100644
--- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
+++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
@@ -970,6 +970,12 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_L3_10078 = "ORG_ZSTACK_NETWORK_L3_10078";
+ public static final String ORG_ZSTACK_NETWORK_L3_10079 = "ORG_ZSTACK_NETWORK_L3_10079";
+
+ public static final String ORG_ZSTACK_NETWORK_L3_10080 = "ORG_ZSTACK_NETWORK_L3_10080";
+
+ public static final String ORG_ZSTACK_NETWORK_L3_10081 = "ORG_ZSTACK_NETWORK_L3_10081";
+
public static final String ORG_ZSTACK_SNS_PLATFORM_UNIVERSALSMS_SUPPLIER_EMAY_10000 = "ORG_ZSTACK_SNS_PLATFORM_UNIVERSALSMS_SUPPLIER_EMAY_10000";
public static final String ORG_ZSTACK_CORE_VALIDATION_10000 = "ORG_ZSTACK_CORE_VALIDATION_10000";
@@ -1016,6 +1022,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_L2_10020 = "ORG_ZSTACK_NETWORK_L2_10020";
+ public static final String ORG_ZSTACK_NETWORK_L2_10021 = "ORG_ZSTACK_NETWORK_L2_10021";
+
public static final String ORG_ZSTACK_CONSOLE_10000 = "ORG_ZSTACK_CONSOLE_10000";
public static final String ORG_ZSTACK_CONSOLE_10001 = "ORG_ZSTACK_CONSOLE_10001";
@@ -1654,6 +1662,10 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_LDAP_10019 = "ORG_ZSTACK_LDAP_10019";
+ public static final String ORG_ZSTACK_LDAP_10020 = "ORG_ZSTACK_LDAP_10020";
+
+ public static final String ORG_ZSTACK_LDAP_10021 = "ORG_ZSTACK_LDAP_10021";
+
public static final String ORG_ZSTACK_IMAGE_10000 = "ORG_ZSTACK_IMAGE_10000";
public static final String ORG_ZSTACK_IMAGE_10001 = "ORG_ZSTACK_IMAGE_10001";
@@ -3230,6 +3242,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10024 = "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10024";
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10025 = "ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10025";
+
public static final String ORG_ZSTACK_STORAGE_DEVICE_10000 = "ORG_ZSTACK_STORAGE_DEVICE_10000";
public static final String ORG_ZSTACK_STORAGE_DEVICE_10001 = "ORG_ZSTACK_STORAGE_DEVICE_10001";
@@ -5478,6 +5492,14 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_BAREMETAL2_GATEWAY_10083 = "ORG_ZSTACK_BAREMETAL2_GATEWAY_10083";
+ public static final String ORG_ZSTACK_BAREMETAL2_GATEWAY_10084 = "ORG_ZSTACK_BAREMETAL2_GATEWAY_10084";
+
+ public static final String ORG_ZSTACK_BAREMETAL2_GATEWAY_10085 = "ORG_ZSTACK_BAREMETAL2_GATEWAY_10085";
+
+ public static final String ORG_ZSTACK_BAREMETAL2_DPU_10000 = "ORG_ZSTACK_BAREMETAL2_DPU_10000";
+
+ public static final String ORG_ZSTACK_BAREMETAL2_DPU_10001 = "ORG_ZSTACK_BAREMETAL2_DPU_10001";
+
public static final String ORG_ZSTACK_STORAGE_PRIMARY_SHAREDBLOCK_10000 = "ORG_ZSTACK_STORAGE_PRIMARY_SHAREDBLOCK_10000";
public static final String ORG_ZSTACK_STORAGE_PRIMARY_SHAREDBLOCK_10001 = "ORG_ZSTACK_STORAGE_PRIMARY_SHAREDBLOCK_10001";
@@ -6274,6 +6296,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";
@@ -6500,6 +6524,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10014 = "ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10014";
+ public static final String ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015 = "ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015";
+
public static final String ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10040 = "ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10040";
public static final String ORG_ZSTACK_NETWORK_HOSTNETWORKINTERFACE_LLDP_10000 = "ORG_ZSTACK_NETWORK_HOSTNETWORKINTERFACE_LLDP_10000";
@@ -7692,6 +7718,12 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10089 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10089";
+ public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10090 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10090";
+
+ public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10091 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10091";
+
+ public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10092 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10092";
+
public static final String ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10000 = "ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10000";
public static final String ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10001 = "ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10001";
@@ -9828,6 +9860,26 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_COMPUTE_VM_10320 = "ORG_ZSTACK_COMPUTE_VM_10320";
+ public static final String ORG_ZSTACK_COMPUTE_VM_10321 = "ORG_ZSTACK_COMPUTE_VM_10321";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10322 = "ORG_ZSTACK_COMPUTE_VM_10322";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10323 = "ORG_ZSTACK_COMPUTE_VM_10323";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10324 = "ORG_ZSTACK_COMPUTE_VM_10324";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10325 = "ORG_ZSTACK_COMPUTE_VM_10325";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10326 = "ORG_ZSTACK_COMPUTE_VM_10326";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10327 = "ORG_ZSTACK_COMPUTE_VM_10327";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10328 = "ORG_ZSTACK_COMPUTE_VM_10328";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10329 = "ORG_ZSTACK_COMPUTE_VM_10329";
+
+ public static final String ORG_ZSTACK_COMPUTE_VM_10330 = "ORG_ZSTACK_COMPUTE_VM_10330";
+
public static final String ORG_ZSTACK_IDENTITY_LOGIN_10000 = "ORG_ZSTACK_IDENTITY_LOGIN_10000";
public static final String ORG_ZSTACK_STORAGE_VOLUME_BLOCK_EXPON_10000 = "ORG_ZSTACK_STORAGE_VOLUME_BLOCK_EXPON_10000";
@@ -10380,6 +10432,7 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_SECURITYGROUP_10129 = "ORG_ZSTACK_NETWORK_SECURITYGROUP_10129";
+
public static final String ORG_ZSTACK_TEMPLATECONFIG_10000 = "ORG_ZSTACK_TEMPLATECONFIG_10000";
public static final String ORG_ZSTACK_TEMPLATECONFIG_10001 = "ORG_ZSTACK_TEMPLATECONFIG_10001";
@@ -10652,6 +10705,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_PROMETHEUS_10013 = "ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_PROMETHEUS_10013";
+ public static final String ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_PROMETHEUS_10014 = "ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_PROMETHEUS_10014";
+
public static final String ORG_ZSTACK_AI_CONTAINER_10000 = "ORG_ZSTACK_AI_CONTAINER_10000";
public static final String ORG_ZSTACK_AI_CONTAINER_10001 = "ORG_ZSTACK_AI_CONTAINER_10001";
@@ -11586,6 +11641,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_PCIDEVICE_10076 = "ORG_ZSTACK_PCIDEVICE_10076";
+ public static final String ORG_ZSTACK_PCIDEVICE_10077 = "ORG_ZSTACK_PCIDEVICE_10077";
+
public static final String ORG_ZSTACK_CAS_DRIVER_DONGHAI_10000 = "ORG_ZSTACK_CAS_DRIVER_DONGHAI_10000";
public static final String ORG_ZSTACK_CAS_DRIVER_DONGHAI_10001 = "ORG_ZSTACK_CAS_DRIVER_DONGHAI_10001";
@@ -13600,6 +13657,16 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10171 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10171";
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10172 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10172";
+
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10173 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10173";
+
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10174 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10174";
+
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10175 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10175";
+
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_LB_10176 = "ORG_ZSTACK_NETWORK_SERVICE_LB_10176";
+
public static final String ORG_ZSTACK_IPSEC_10000 = "ORG_ZSTACK_IPSEC_10000";
public static final String ORG_ZSTACK_IPSEC_10001 = "ORG_ZSTACK_IPSEC_10001";
@@ -13730,6 +13797,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_BAREMETAL2_CHASSIS_10025 = "ORG_ZSTACK_BAREMETAL2_CHASSIS_10025";
+ public static final String ORG_ZSTACK_BAREMETAL2_CHASSIS_10026 = "ORG_ZSTACK_BAREMETAL2_CHASSIS_10026";
+
public static final String ORG_ZSTACK_BAREMETAL2_CLUSTER_10000 = "ORG_ZSTACK_BAREMETAL2_CLUSTER_10000";
public static final String ORG_ZSTACK_BAREMETAL2_CLUSTER_10001 = "ORG_ZSTACK_BAREMETAL2_CLUSTER_10001";
@@ -14802,6 +14871,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_AI_10133 = "ORG_ZSTACK_AI_10133";
+ public static final String ORG_ZSTACK_AI_10134 = "ORG_ZSTACK_AI_10134";
+
public static final String ORG_ZSTACK_CORE_CLOUDBUS_10000 = "ORG_ZSTACK_CORE_CLOUDBUS_10000";
public static final String ORG_ZSTACK_CORE_CLOUDBUS_10001 = "ORG_ZSTACK_CORE_CLOUDBUS_10001";
@@ -15118,6 +15189,8 @@ public class CloudOperationsErrorCode {
public static final String ORG_ZSTACK_NETWORK_SERVICE_EIP_10023 = "ORG_ZSTACK_NETWORK_SERVICE_EIP_10023";
+ public static final String ORG_ZSTACK_NETWORK_SERVICE_EIP_10024 = "ORG_ZSTACK_NETWORK_SERVICE_EIP_10024";
+
public static final String ORG_ZSTACK_TEST_INTEGRATION_GUESTTOOLS_10000 = "ORG_ZSTACK_TEST_INTEGRATION_GUESTTOOLS_10000";
public static final String ORG_ZSTACK_TEST_INTEGRATION_GUESTTOOLS_10001 = "ORG_ZSTACK_TEST_INTEGRATION_GUESTTOOLS_10001";
diff --git a/utils/src/main/java/org/zstack/utils/network/NicIpAddressInfo.java b/utils/src/main/java/org/zstack/utils/network/NicIpAddressInfo.java
index 9803946ee25..f190a2f278b 100644
--- a/utils/src/main/java/org/zstack/utils/network/NicIpAddressInfo.java
+++ b/utils/src/main/java/org/zstack/utils/network/NicIpAddressInfo.java
@@ -1,5 +1,7 @@
package org.zstack.utils.network;
+import java.util.List;
+
public class NicIpAddressInfo {
public String ipv4Address;
public String ipv4Gateway;
@@ -7,6 +9,7 @@ public class NicIpAddressInfo {
public String ipv6Address;
public String ipv6Gateway;
public String ipv6Prefix;
+ public List dnsAddresses;
public NicIpAddressInfo(String ipv4Address, String ipv4Gateway, String ipv4Netmask, String ipv6Address, String ipv6Gateway, String ipv6Prefix) {
this.ipv4Address = ipv4Address;
@@ -15,5 +18,6 @@ public NicIpAddressInfo(String ipv4Address, String ipv4Gateway, String ipv4Netma
this.ipv6Address = ipv6Address;
this.ipv6Gateway = ipv6Gateway;
this.ipv6Prefix = ipv6Prefix;
+ this.dnsAddresses = null;
}
}
diff --git a/utils/src/main/java/org/zstack/utils/string/StringSimilarity.java b/utils/src/main/java/org/zstack/utils/string/StringSimilarity.java
index ee5257d1640..be5d1a5442f 100644
--- a/utils/src/main/java/org/zstack/utils/string/StringSimilarity.java
+++ b/utils/src/main/java/org/zstack/utils/string/StringSimilarity.java
@@ -32,6 +32,9 @@ public class StringSimilarity {
private static final int mapLength = 1500;
public static int maxElaborationRegex = 8192;
+ // slow elaboration warn threshold in ms; 0 = disabled (default)
+ // enable via GlobalProperty or test setup: StringSimilarity.slowElaborationThresholdMs = 200;
+ public static long slowElaborationThresholdMs = 0;
// matched errors
private static final Map errors = new LinkedHashMap(mapLength, 0.9f, true) {
@@ -280,13 +283,29 @@ private static boolean isRedundant(String sub) {
}
private static void logSearchSpend(String sub, long start, boolean found) {
- logger.debug(String.format("[%s] spend %s ms to search elaboration \"%s\"", found, System.currentTimeMillis() - start,
- sub.length() > 50 ? sub.substring(0 , 50) + "..." : sub));
+ long cost = System.currentTimeMillis() - start;
+ if (slowElaborationThresholdMs > 0 && cost > slowElaborationThresholdMs) {
+ logger.warn(String.format("[SLOW-ELABORATION] cost %d ms, found=%s, input_len=%d, input=%s",
+ cost, found, sub.length(),
+ sub.length() > 100 ? sub.substring(0, 100) + "..." : sub));
+ } else {
+ logger.debug(String.format("[%s] spend %s ms to search elaboration \"%s\"", found, cost,
+ sub.length() > 50 ? sub.substring(0, 50) + "..." : sub));
+ }
}
/**
* find the most similar error code elaboration for the given error message.
*
+ * The method uses a two-phase strategy to avoid performance degradation
+ * when format args produce very long strings (e.g., serialized error chains
+ * or HTML response bodies):
+ *
+ * Phase 1: Try regex matching with the formatted string (length-guarded).
+ * Phase 2: If Phase 1 misses (or formatted string too long), fallback
+ * to the raw fmt template for regex matching.
+ * Phase 3: Distance matching always uses the raw fmt template.
+ *
* @param sub error message or error message fmt
* @param args arguments
* @return the most similar error code elaboration
@@ -297,7 +316,10 @@ public static ErrorCodeElaboration findSimilar(String sub, Object...args) {
}
long start = System.currentTimeMillis();
- ErrorCodeElaboration err = errors.get(sub);
+ ErrorCodeElaboration err;
+ synchronized (errors) {
+ err = errors.get(sub);
+ }
if (err != null && verifyElaboration(err, sub, args)) {
logSearchSpend(sub, start, true);
return err;
@@ -308,26 +330,50 @@ public static ErrorCodeElaboration findSimilar(String sub, Object...args) {
// same cause will happen
// invalid cache, generate elaboration again
if (err != null) {
- errors.remove(sub);
+ synchronized (errors) {
+ errors.remove(sub);
+ }
}
- if (args != null && missed.get(String.format(sub, args)) != null) {
- logSearchSpend(sub, start, false);
- return null;
- } else if (missed.get(sub) != null) {
- logSearchSpend(sub, start, false);
- return null;
+ // check missed cache for both fmt template and formatted string
+ synchronized (missed) {
+ if (missed.get(sub) != null) {
+ logSearchSpend(sub, start, false);
+ return null;
+ }
+ }
+ if (args != null) {
+ try {
+ String formatted = String.format(sub, args);
+ synchronized (missed) {
+ if (missed.get(formatted) != null) {
+ logSearchSpend(sub, start, false);
+ return null;
+ }
+ }
+ } catch (Exception e) {
+ logger.trace(String.format("failed to format elaboration key: %s", e.getMessage()));
+ }
}
- try {
- logger.trace(String.format("start to search elaboration for: %s", String.format(sub, args)));
- err = findMostSimilarRegex(String.format(sub, args));
- } catch (Exception e) {
- logger.trace(String.format("start search elaboration for: %s", sub));
+ // Phase 1: try regex matching with formatted string (guarded by length limit)
+ if (args != null && args.length > 0) {
+ try {
+ String formatted = String.format(sub, args);
+ if (formatted.length() <= maxElaborationRegex) {
+ err = findMostSimilarRegex(formatted);
+ }
+ } catch (Exception e) {
+ logger.trace(String.format("failed to format for regex matching: %s", e.getMessage()));
+ }
+ }
+
+ // Phase 2: if formatted string missed or was too long, fallback to raw fmt template
+ if (err == null) {
err = findMostSimilarRegex(sub);
}
- // find by distance is not reliable disable it for now
+ // Phase 3: distance matching uses the raw fmt template (always short)
if (err == null) {
err = findSimilarDistance(sub);
}
@@ -355,6 +401,10 @@ private static boolean verifyElaboration(ErrorCodeElaboration elaboration, Strin
// better precision, worse performance
private static ErrorCodeElaboration findMostSimilarRegex(String sub) {
+ if (sub.length() > maxElaborationRegex) {
+ return null;
+ }
+
if (!isRegexMatchedByRetrees(sub)) {
return null;
}