From eb24c9f57001dcfd6bff31e8b4d06c9f178faff3 Mon Sep 17 00:00:00 2001 From: "shixin.ruan" Date: Mon, 27 Jul 2026 19:47:17 +0900 Subject: [PATCH] [management-network]: fix callback endpoint selection Resolve hostnames once and keep callback address family aligned. Resolves: ZSTAC-86635 Change-Id: Ie4a0247e78f4f557e32df0eae491a2ebe8d45a72 --- .../main/java/org/zstack/core/Platform.java | 75 +++++++++++++++++ .../zstack/core/ansible/AnsibleRunner.java | 23 +++--- .../org/zstack/core/rest/RESTFacadeImpl.java | 72 +++++++++++++---- .../src/main/java/org/zstack/kvm/KVMHost.java | 56 +++++++------ ...formManagementEndpointSelectionTest.groovy | 81 +++++++++++++++++++ ...sibleRunnerTargetAwareArgumentsTest.groovy | 9 +-- .../CloudOperationsErrorCode.java | 10 +++ 7 files changed, 265 insertions(+), 61 deletions(-) diff --git a/core/src/main/java/org/zstack/core/Platform.java b/core/src/main/java/org/zstack/core/Platform.java index 8f9fe68068d..51c785babbd 100755 --- a/core/src/main/java/org/zstack/core/Platform.java +++ b/core/src/main/java/org/zstack/core/Platform.java @@ -76,6 +76,9 @@ import static org.zstack.utils.CollectionDSL.e; import static org.zstack.utils.CollectionDSL.map; import static org.zstack.utils.StringDSL.ln; +import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_CORE_PLATFORM_10003; +import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_CORE_PLATFORM_10004; +import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_CORE_PLATFORM_10005; public class Platform { private static final CLogger logger = CLoggerImpl.getLogger(Platform.class); @@ -1058,6 +1061,78 @@ public static ErrorableValue getManagementServerIp(String targetIp) { return getManagementNodeEndpointData().selectForTarget(ManagementEndpointData.EndpointType.NODE, targetIp); } + public static ErrorableValue> resolveRemoteEndpoints(String endpoint) { + String normalizedEndpoint = IPv6NetworkUtils.stripHostUrlBrackets(endpoint == null ? null : endpoint.trim()); + if (StringUtils.isBlank(normalizedEndpoint)) { + return ErrorableValue.ofErrorCode(argerr(ORG_ZSTACK_CORE_PLATFORM_10003, + "cannot resolve an empty remote endpoint")); + } + + if (NetworkUtils.isIpAddress(normalizedEndpoint)) { + return resolveRemoteEndpoints(normalizedEndpoint, Collections.singletonList(normalizedEndpoint)); + } + + try { + List addresses = Arrays.stream(InetAddress.getAllByName(normalizedEndpoint)) + .map(InetAddress::getHostAddress) + .collect(Collectors.toList()); + return resolveRemoteEndpoints(normalizedEndpoint, addresses); + } catch (UnknownHostException e) { + return ErrorableValue.ofErrorCode(operr(ORG_ZSTACK_CORE_PLATFORM_10004, + "cannot resolve remote endpoint[%s]: %s", normalizedEndpoint, e.getMessage())); + } + } + + public static ErrorableValue> resolveRemoteEndpoints(String endpoint, Collection addresses) { + List endpoints = new ArrayList<>(); + ErrorCode lastError = null; + Set uniqueAddresses = new LinkedHashSet<>(); + if (addresses != null) { + for (String address : addresses) { + String connectIp = normalizeManagementIp(IPv6NetworkUtils.stripHostUrlBrackets(address)); + if (!NetworkUtils.isIpAddress(connectIp) || !uniqueAddresses.add(connectIp)) { + continue; + } + + ErrorableValue callbackIp = getManagementServerIp(connectIp); + if (callbackIp.isSuccess()) { + endpoints.add(new RemoteEndpoint(connectIp, callbackIp.result)); + } else { + lastError = callbackIp.error; + } + } + } + + if (!endpoints.isEmpty()) { + return ErrorableValue.of(endpoints); + } + + if (lastError != null) { + return ErrorableValue.ofErrorCode(lastError); + } + + return ErrorableValue.ofErrorCode(argerr(ORG_ZSTACK_CORE_PLATFORM_10005, + "remote endpoint[%s] has no valid IPv4 or IPv6 address", endpoint)); + } + + public static class RemoteEndpoint { + private final String connectIp; + private final String callbackIp; + + public RemoteEndpoint(String connectIp, String callbackIp) { + this.connectIp = connectIp; + this.callbackIp = callbackIp; + } + + public String getConnectIp() { + return connectIp; + } + + public String getCallbackIp() { + return callbackIp; + } + } + public static int getManagementNodeServicePort() { return Integer.parseInt(System.getProperty("RESTFacade.port", "8080")); } diff --git a/core/src/main/java/org/zstack/core/ansible/AnsibleRunner.java b/core/src/main/java/org/zstack/core/ansible/AnsibleRunner.java index f387447885f..898301d92fa 100755 --- a/core/src/main/java/org/zstack/core/ansible/AnsibleRunner.java +++ b/core/src/main/java/org/zstack/core/ansible/AnsibleRunner.java @@ -390,22 +390,19 @@ private void cleanup() { public void run(ReturnValueCompletion completion) { try { + ErrorableValue> resolvedEndpoints = Platform.resolveRemoteEndpoints(targetIp); + if (!resolvedEndpoints.isSuccess()) { + completion.fail(resolvedEndpoints.error); + return; + } + Platform.RemoteEndpoint resolvedEndpoint = resolvedEndpoints.result.get(0); + targetIp = resolvedEndpoint.getConnectIp(); + String selectedManagementNodeIp = managementNodeIp; if (selectedManagementNodeIp == null) { - if (NetworkUtils.isIpAddress(targetIp)) { - ErrorableValue managementNodeEndpoint = Platform.getManagementServerIp(targetIp); - if (!managementNodeEndpoint.isSuccess()) { - completion.fail(managementNodeEndpoint.error); - return; - } - selectedManagementNodeIp = managementNodeEndpoint.result; - } else { - selectedManagementNodeIp = restf.getHostName(); - } - } - if (NetworkUtils.isIpAddress(targetIp)) { - updateCheckersManagementNodeIp(selectedManagementNodeIp); + selectedManagementNodeIp = resolvedEndpoint.getCallbackIp(); } + updateCheckersManagementNodeIp(selectedManagementNodeIp); if (!forceRun && !isNeedRun()) { completion.success(false); diff --git a/core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java b/core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java index 7d5f3bb745a..1ef10483a21 100755 --- a/core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java +++ b/core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java @@ -243,11 +243,7 @@ public static String buildCallbackUrl(String hostName, int port, String path) { } public static ErrorableValue selectCallbackUrl(String requestUrl, Map headers, String defaultCallbackUrl, int port, String path) { - if (headers != null && headers.keySet().stream().anyMatch(RESTConstant.CALLBACK_URL::equalsIgnoreCase)) { - return ErrorableValue.of(defaultCallbackUrl); - } - - if (CoreGlobalProperty.UNIT_TEST_ON) { + if (hasCallbackUrlHeader(headers) || CoreGlobalProperty.UNIT_TEST_ON) { return ErrorableValue.of(defaultCallbackUrl); } @@ -256,15 +252,41 @@ public static ErrorableValue selectCallbackUrl(String requestUrl, Map endpoint = resolveRequestEndpoint(host); + if (!endpoint.isSuccess()) { + return ErrorableValue.ofErrorCode(endpoint.error); + } + return ErrorableValue.of(buildCallbackUrl(endpoint.result.getCallbackIp(), port, path)); + } + + private static boolean hasCallbackUrlHeader(Map headers) { + return headers != null && headers.keySet().stream().anyMatch(RESTConstant.CALLBACK_URL::equalsIgnoreCase); + } + + private static ErrorableValue resolveRequestEndpoint(String host) { + ErrorableValue> endpoints = Platform.resolveRemoteEndpoints(host); + if (!endpoints.isSuccess()) { + return ErrorableValue.ofErrorCode(endpoints.error); } + return ErrorableValue.of(endpoints.result.get(0)); + } + + private static boolean isHttpsHostname(String requestUrl, String host) { + try { + return "https".equalsIgnoreCase(new URI(requestUrl).getScheme()) && !NetworkUtils.isIpAddress(host); + } catch (URISyntaxException e) { + return false; + } + } - ErrorableValue callbackIp = Platform.getManagementServerIp(host); - if (!callbackIp.isSuccess()) { - return ErrorableValue.ofErrorCode(callbackIp.error); + private static String replaceRequestHost(String requestUrl, String connectIp) { + try { + URI uri = new URI(requestUrl); + return new URI(uri.getScheme(), uri.getUserInfo(), connectIp, uri.getPort(), + uri.getPath(), uri.getQuery(), uri.getFragment()).toString(); + } catch (URISyntaxException e) { + throw new CloudRuntimeException(String.format("cannot replace request host in url[%s]", requestUrl), e); } - return ErrorableValue.of(buildCallbackUrl(callbackIp.result, port, path)); } private static String extractRequestHost(String requestUrl) { @@ -434,11 +456,27 @@ public void asyncJsonGet(final String url, final String body, Map headers, HttpMethod method, final AsyncRESTCallback callback, final TimeUnit unit, final long timeout) { - ErrorableValue selectedCallbackUrl = selectCallbackUrl(url, headers, callbackUrl, port, path); - if (!selectedCallbackUrl.isSuccess()) { - callback.fail(selectedCallbackUrl.error); - return; + String selectedCallbackUrl = callbackUrl; + String targetUrl = url; + if (!hasCallbackUrlHeader(headers) && !CoreGlobalProperty.UNIT_TEST_ON) { + String host = extractRequestHost(url); + if (host != null) { + if (isHttpsHostname(url, host)) { + callback.fail(operr(ORG_ZSTACK_CORE_REST_10016, + "cannot use hostname[%s] for asynchronous HTTPS request[%s] because its resolved address cannot be pinned without changing TLS server identity", + host, url)); + return; + } + ErrorableValue endpoint = resolveRequestEndpoint(host); + if (!endpoint.isSuccess()) { + callback.fail(endpoint.error); + return; + } + selectedCallbackUrl = buildCallbackUrl(endpoint.result.getCallbackIp(), port, path); + targetUrl = replaceRequestHost(url, endpoint.result.getConnectIp()); + } } + final String actualTargetUrl = targetUrl; synchronized (interceptors) { for (BeforeAsyncJsonPostInterceptor ic : interceptors) { @@ -469,7 +507,7 @@ public void asyncJson(final String url, final String body, Map h HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.setContentLength(body.length()); requestHeaders.set(RESTConstant.TASK_UUID, taskUuid); - requestHeaders.set(RESTConstant.CALLBACK_URL, selectedCallbackUrl.result); + requestHeaders.set(RESTConstant.CALLBACK_URL, selectedCallbackUrl); MediaType JSON = MediaType.parseMediaType("application/json; charset=utf-8"); requestHeaders.setContentType(JSON); if (headers != null) { @@ -667,7 +705,7 @@ public long getSentAtMillis() { logger.trace(String.format("json %s [%s], %s", method.toString(), url, req)); } - ListenableFuture> f = asyncRestTemplate.exchange(url, method, req, String.class); + ListenableFuture> f = asyncRestTemplate.exchange(actualTargetUrl, method, req, String.class); f.addCallback(rsp -> {}, e -> wrapper.fail(err(ORG_ZSTACK_CORE_REST_10003, SysErrors.HTTP_ERROR, e.getLocalizedMessage()))); } catch (RestClientException e) { logger.warn(String.format("Unable to %s to %s: %s", method.toString(), url, e.getMessage())); diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java index 2d98423bb40..37e8d686fba 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java @@ -2904,11 +2904,11 @@ public static String buildManagementNodeCallbackCheckCommand(String callbackUrl) } public static ErrorableValue buildManagementNodeCallbackCheckCommand(String hostManagementIp, RESTFacade restf) { - ErrorableValue callbackHost = Platform.getManagementServerIp(hostManagementIp); - if (!callbackHost.isSuccess()) { - return ErrorableValue.ofErrorCode(callbackHost.error); + ErrorableValue> endpoints = Platform.resolveRemoteEndpoints(hostManagementIp); + if (!endpoints.isSuccess()) { + return ErrorableValue.ofErrorCode(endpoints.error); } - return ErrorableValue.of(buildManagementNodeCallbackCheckCommand(restf.buildCallbackUrl(callbackHost.result))); + return ErrorableValue.of(buildManagementNodeCallbackCheckCommand(restf.buildCallbackUrl(endpoints.result.get(0).getCallbackIp()))); } private static String joinAgentPath(String rootPath, String path) { @@ -5804,18 +5804,19 @@ public boolean skip(Map data) { @Override public void run(FlowTrigger trigger, Map data) { - ShellUtils.run(String.format("arp -d %s || true", getSelf().getManagementIp())); + ErrorableValue> endpoints = Platform.resolveRemoteEndpoints(getSelf().getManagementIp()); + if (!endpoints.isSuccess()) { + throw new OperationFailureException(endpoints.error); + } + Platform.RemoteEndpoint endpoint = endpoints.result.get(0); + ShellUtils.run(String.format("arp -d %s || true", endpoint.getConnectIp())); SshShell sshShell = new SshShell(); - sshShell.setHostname(getSelf().getManagementIp()); + sshShell.setHostname(endpoint.getConnectIp()); sshShell.setUsername(getSelf().getUsername()); sshShell.setPassword(getSelf().getPassword()); sshShell.setPort(getSelf().getPort()); sshShell.setWithSudo(false); - ErrorableValue callbackHost = Platform.getManagementServerIp(getSelf().getManagementIp()); - if (!callbackHost.isSuccess()) { - throw new OperationFailureException(callbackHost.error); - } - final String cmd = buildManagementNodeCallbackCheckCommand(restf.buildCallbackUrl(callbackHost.result)); + final String cmd = buildManagementNodeCallbackCheckCommand(restf.buildCallbackUrl(endpoint.getCallbackIp())); SshResult ret = sshShell.runCommand(cmd); if (ret.getStderr() != null && ret.getStderr().contains("No route to host")) { // c.f. https://access.redhat.com/solutions/1120533 @@ -5833,7 +5834,7 @@ public void run(FlowTrigger trigger, Map data) { "please check if username/password is wrong; %s", self.getManagementIp(), getSelf().getUsername(), getSelf().getPort(), ret.getExitErrorMessage())); } else if (ret.getReturnCode() != 0) { throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10106, "the KVM host[ip:%s] cannot access the management node's callback url. It seems" + - " that the KVM host cannot reach the management IP[%s]. %s %s", self.getManagementIp(), callbackHost.result, + " that the KVM host cannot reach the management IP[%s]. %s %s", self.getManagementIp(), endpoint.getCallbackIp(), ret.getStderr(), ret.getExitErrorMessage())); } @@ -6069,11 +6070,14 @@ public boolean skip(Map data) { @Override public void run(final FlowTrigger trigger, Map data) { - ErrorableValue callbackIp = Platform.getManagementServerIp(getSelf().getManagementIp()); - if (!callbackIp.isSuccess()) { - trigger.fail(callbackIp.error); + ErrorableValue> endpoints = Platform.resolveRemoteEndpoints(getSelf().getManagementIp()); + if (!endpoints.isSuccess()) { + trigger.fail(endpoints.error); return; } + Platform.RemoteEndpoint endpoint = endpoints.result.get(0); + String hostManagementIp = endpoint.getConnectIp(); + String callbackIp = endpoint.getCallbackIp(); String srcPath = PathUtil.findFileOnClassPath(String.format("ansible/kvm/%s", agentPackageName), true).getAbsolutePath(); String destPath = String.format("/var/lib/zstack/kvm/package/%s", agentPackageName); @@ -6081,7 +6085,7 @@ public void run(final FlowTrigger trigger, Map data) { checker.setUsername(getSelf().getUsername()); checker.setPassword(getSelf().getPassword()); checker.setSshPort(getSelf().getPort()); - checker.setTargetIp(getSelf().getManagementIp()); + checker.setTargetIp(hostManagementIp); checker.addSrcDestPair(SshFileMd5Checker.ZSTACKLIB_SRC_PATH, String.format("/var/lib/zstack/kvm/package/%s", AnsibleGlobalProperty.ZSTACKLIB_PACKAGE_NAME)); checker.addSrcDestPair(srcPath, destPath); @@ -6089,31 +6093,31 @@ public void run(final FlowTrigger trigger, Map data) { dhcpChecker.setUsername(getSelf().getUsername()); dhcpChecker.setPassword(getSelf().getPassword()); dhcpChecker.setSshPort(getSelf().getPort()); - dhcpChecker.setTargetIp(getSelf().getManagementIp()); + dhcpChecker.setTargetIp(hostManagementIp); dhcpChecker.setFilePath(KVMConstant.DHCP_BIN_FILE_PATH); SshChronyConfigChecker chronyChecker = new SshChronyConfigChecker(); - chronyChecker.setTargetIp(getSelf().getManagementIp()); + chronyChecker.setTargetIp(hostManagementIp); chronyChecker.setUsername(getSelf().getUsername()); chronyChecker.setPassword(getSelf().getPassword()); chronyChecker.setSshPort(getSelf().getPort()); SshYumRepoChecker repoChecker = new SshYumRepoChecker(); - repoChecker.setTargetIp(getSelf().getManagementIp()); + repoChecker.setTargetIp(hostManagementIp); repoChecker.setUsername(getSelf().getUsername()); repoChecker.setPassword(getSelf().getPassword()); repoChecker.setSshPort(getSelf().getPort()); CallBackNetworkChecker callbackChecker = new CallBackNetworkChecker(); - callbackChecker.setTargetIp(getSelf().getManagementIp()); + callbackChecker.setTargetIp(hostManagementIp); callbackChecker.setUsername(getSelf().getUsername()); callbackChecker.setPassword(getSelf().getPassword()); callbackChecker.setPort(getSelf().getPort()); - callbackChecker.setCallbackIp(callbackIp.result); + callbackChecker.setCallbackIp(callbackIp); callbackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT); KvmHostConfigChecker kvmHostConfigChecker = new KvmHostConfigChecker(); - kvmHostConfigChecker.setTargetIp(getSelf().getManagementIp()); + kvmHostConfigChecker.setTargetIp(hostManagementIp); kvmHostConfigChecker.setUsername(getSelf().getUsername()); kvmHostConfigChecker.setPassword(getSelf().getPassword()); kvmHostConfigChecker.setSshPort(getSelf().getPort()); @@ -6129,11 +6133,11 @@ public void run(final FlowTrigger trigger, Map data) { if (KVMGlobalConfig.ENABLE_HOST_TCP_CONNECTION_CHECK.value(Boolean.class)) { CallBackNetworkChecker hostTcpConnectionCallbackChecker = new CallBackNetworkChecker(); - hostTcpConnectionCallbackChecker.setTargetIp(getSelf().getManagementIp()); + hostTcpConnectionCallbackChecker.setTargetIp(hostManagementIp); hostTcpConnectionCallbackChecker.setUsername(getSelf().getUsername()); hostTcpConnectionCallbackChecker.setPassword(getSelf().getPassword()); hostTcpConnectionCallbackChecker.setPort(getSelf().getPort()); - hostTcpConnectionCallbackChecker.setCallbackIp(callbackIp.result); + hostTcpConnectionCallbackChecker.setCallbackIp(callbackIp); hostTcpConnectionCallbackChecker.setCallBackPort(KVMGlobalProperty.TCP_SERVER_PORT); runner.installChecker(hostTcpConnectionCallbackChecker); } @@ -6145,8 +6149,8 @@ public void run(final FlowTrigger trigger, Map data) { } } runner.setAgentPort(KVMGlobalProperty.AGENT_PORT); - runner.setTargetIp(getSelf().getManagementIp()); - runner.setManagementNodeIp(callbackIp.result); + runner.setTargetIp(hostManagementIp); + runner.setManagementNodeIp(callbackIp); runner.setTargetUuid(getSelf().getUuid()); runner.setPlayBookName(KVMConstant.ANSIBLE_PLAYBOOK_NAME); runner.setUsername(getSelf().getUsername()); diff --git a/test/src/test/groovy/org/zstack/test/integration/core/PlatformManagementEndpointSelectionTest.groovy b/test/src/test/groovy/org/zstack/test/integration/core/PlatformManagementEndpointSelectionTest.groovy index be88168b012..422b5fe7658 100644 --- a/test/src/test/groovy/org/zstack/test/integration/core/PlatformManagementEndpointSelectionTest.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/core/PlatformManagementEndpointSelectionTest.groovy @@ -115,6 +115,41 @@ class PlatformManagementEndpointSelectionTest { } } + @Test + void testResolvedRemoteEndpointsKeepConnectionAndCallbackInTheSameAddressFamily() { + withUnitTestOn(false) { + withErrorFacade { + withManagementServerIpProperties([ + "management.server.ip" : IPV4, + "management.server.ip6": IPV6, + ]) { + def endpoints = Platform.resolveRemoteEndpoints("controller.example.com", ["2001:db8::20", "192.168.1.20"]) + + assert endpoints.success + assert endpoints.result*.connectIp == ["2001:db8::20", "192.168.1.20"] + assert endpoints.result*.callbackIp == [IPV6, IPV4] + } + } + } + } + + @Test + void testResolvedRemoteEndpointsSkipAddressFamiliesWithoutManagementEndpoint() { + withUnitTestOn(false) { + withErrorFacade { + withManagementServerIpProperties([ + "management.server.ip": IPV4, + ]) { + def endpoints = Platform.resolveRemoteEndpoints("controller.example.com", ["2001:db8::20", "192.168.1.20"]) + + assert endpoints.success + assert endpoints.result*.connectIp == ["192.168.1.20"] + assert endpoints.result*.callbackIp == [IPV4] + } + } + } + } + @Test void testTargetAwareGetterFailsWithoutConfiguredEndpointOutsideUnitTest() { withUnitTestOn(false) { @@ -158,6 +193,23 @@ class PlatformManagementEndpointSelectionTest { } } + @Test + void testAsyncRestCallbackSelectionResolvesHostnameToMatchingManagementEndpoint() { + withUnitTestOn(false) { + withErrorFacade { + withManagementServerIpProperties([ + "management.server.ip": IPV4, + ]) { + def callback = RESTFacadeImpl.selectCallbackUrl( + "http://localhost:7070/host/ping", [:], "http://127.0.0.1:8080/zstack/asyncrest/callback", 8080, "zstack") + + assert callback.success + assert callback.result == "http://${IPV4}:8080/zstack/asyncrest/callback" + } + } + } + } + @Test void testAsyncRestCallbackSelectionFailsBeforeInterceptors() { withUnitTestOn(false) { @@ -191,6 +243,35 @@ class PlatformManagementEndpointSelectionTest { } } + @Test + void testAsyncRestRejectsHttpsHostnameBeforeInterceptors() { + withUnitTestOn(false) { + withErrorFacade { + AtomicInteger interceptorCalls = new AtomicInteger() + AtomicReference error = new AtomicReference() + RESTFacadeImpl restf = new RESTFacadeImpl() + restf.installBeforeAsyncJsonPostInterceptor([ + beforeAsyncJsonPost: { Object... ignored -> interceptorCalls.incrementAndGet() }, + ] as org.zstack.header.rest.BeforeAsyncJsonPostInterceptor) + + restf.asyncJson("https://controller.example.com:7070/host/ping", "{}", [:], HttpMethod.POST, + new AsyncRESTCallback(null) { + @Override + void fail(org.zstack.header.errorcode.ErrorCode errorCode) { + error.set(errorCode) + } + + @Override + void success(HttpEntity responseEntity) { + } + }, TimeUnit.SECONDS, 10) + + assert interceptorCalls.get() == 0 + assert error.get().globalErrorCode == "ORG_ZSTACK_CORE_REST_10016" + } + } + } + private static void withUnitTestOn(boolean unitTestOn, Closure closure) { Class.forName(Platform.class.name) Class.forName(RESTFacadeImpl.class.name) diff --git a/test/src/test/groovy/org/zstack/test/integration/core/ansible/AnsibleRunnerTargetAwareArgumentsTest.groovy b/test/src/test/groovy/org/zstack/test/integration/core/ansible/AnsibleRunnerTargetAwareArgumentsTest.groovy index 24c1277b7f9..5ea3e996666 100644 --- a/test/src/test/groovy/org/zstack/test/integration/core/ansible/AnsibleRunnerTargetAwareArgumentsTest.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/core/ansible/AnsibleRunnerTargetAwareArgumentsTest.groovy @@ -38,7 +38,7 @@ class AnsibleRunnerTargetAwareArgumentsTest { testTargetAwareEndpointArgumentsUseTheSelectedIpv4Node() testMissingFamilyFailsBeforeRunAnsibleDispatch() testMissingFamilyFailsBeforeChecker() - testHostnameKeepsConfiguredCallbackIp() + testHostnameUsesResolvedTargetAndMatchingCallbackIp() } void testTargetAwareEndpointArgumentsUseTheSelectedIpv6Node() { @@ -149,13 +149,12 @@ class AnsibleRunnerTargetAwareArgumentsTest { } } - void testHostnameKeepsConfiguredCallbackIp() { + void testHostnameUsesResolvedTargetAndMatchingCallbackIp() { withManagementServerIpProperties([ "management.server.ip": IPV4, ]) { AtomicReference result = new AtomicReference<>() CallBackNetworkChecker checker = new CallBackNetworkChecker() - checker.callbackIp = "configured-callback-ip" AnsibleRunner runner = new AnsibleRunner() setField(runner, "restf", [ getBaseUrl : { String.format("http://127.0.0.1:%d", REST_PORT) }, @@ -172,7 +171,7 @@ class AnsibleRunnerTargetAwareArgumentsTest { try { CoreGlobalProperty.UNIT_TEST_ON = true runner.forceRun = true - runner.targetIp = "host.example.com" + runner.targetIp = "localhost" runner.targetUuid = Platform.uuid runner.playBookName = "kvm.yml" runner.username = "root" @@ -194,7 +193,7 @@ class AnsibleRunnerTargetAwareArgumentsTest { } assert result.get() == true - assert checker.callbackIp == "configured-callback-ip" + assert checker.callbackIp == IPV4 } } 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 55c5bd0de2c..dae19ccd889 100644 --- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java +++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java @@ -2956,6 +2956,12 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_CORE_PLATFORM_10002 = "ORG_ZSTACK_CORE_PLATFORM_10002"; + public static final String ORG_ZSTACK_CORE_PLATFORM_10003 = "ORG_ZSTACK_CORE_PLATFORM_10003"; + + public static final String ORG_ZSTACK_CORE_PLATFORM_10004 = "ORG_ZSTACK_CORE_PLATFORM_10004"; + + public static final String ORG_ZSTACK_CORE_PLATFORM_10005 = "ORG_ZSTACK_CORE_PLATFORM_10005"; + public static final String ORG_ZSTACK_SCHEDULER_10000 = "ORG_ZSTACK_SCHEDULER_10000"; public static final String ORG_ZSTACK_SCHEDULER_10001 = "ORG_ZSTACK_SCHEDULER_10001"; @@ -10707,6 +10713,8 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_CORE_REST_10015 = "ORG_ZSTACK_CORE_REST_10015"; + public static final String ORG_ZSTACK_CORE_REST_10016 = "ORG_ZSTACK_CORE_REST_10016"; + public static final String ORG_ZSTACK_LICENSE_10000 = "ORG_ZSTACK_LICENSE_10000"; public static final String ORG_ZSTACK_LICENSE_10001 = "ORG_ZSTACK_LICENSE_10001"; @@ -12252,6 +12260,7 @@ public class CloudOperationsErrorCode { // 10052 ZNS internal change-controller endpoint is not a literal IP address // 10053 ZNS change-controller has no computer manager UUID // 10054 ZNS change-controller cluster has no same-family endpoint + // 10055 ZNS endpoint cannot be resolved for an asynchronous request public static final String ORG_ZSTACK_NETWORK_ZNS_10035 = "ORG_ZSTACK_NETWORK_ZNS_10035"; public static final String ORG_ZSTACK_NETWORK_ZNS_10036 = "ORG_ZSTACK_NETWORK_ZNS_10036"; public static final String ORG_ZSTACK_NETWORK_ZNS_10037 = "ORG_ZSTACK_NETWORK_ZNS_10037"; @@ -12272,6 +12281,7 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_NETWORK_ZNS_10052 = "ORG_ZSTACK_NETWORK_ZNS_10052"; public static final String ORG_ZSTACK_NETWORK_ZNS_10053 = "ORG_ZSTACK_NETWORK_ZNS_10053"; public static final String ORG_ZSTACK_NETWORK_ZNS_10054 = "ORG_ZSTACK_NETWORK_ZNS_10054"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10055 = "ORG_ZSTACK_NETWORK_ZNS_10055"; public static final String ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_MARKETPLACE_10000 = "ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_MARKETPLACE_10000";