You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2026/08/28 14:46:26 UTC
(cloudstack) 01/03: Store last successful MS host for agent reconnection fallback
This is an automated email from the ASF dual-hosted git repository.
Pearl1594 pushed a commit to branch store-lastsuccessful-MS
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit a4d05c29ceef6844a85ac1be2455cda9d3fd676c
Author: mprokopchuk <mp...@gmail.com>
AuthorDate: Wed Apr 1 19:01:42 2026 +0200
Store last successful MS host for agent reconnection fallback
* Store last successful MS host for agent reconnection fallback
* Add newline at end of CapacityManagerImplTest.java
---
agent/src/main/java/com/cloud/agent/Agent.java | 18 ++++++++++++-
.../src/main/java/com/cloud/agent/AgentShell.java | 30 +++++++++++++++++++++-
.../src/main/java/com/cloud/agent/IAgentShell.java | 9 +++++++
.../cloud/agent/properties/AgentProperties.java | 8 ++++++
agent/src/test/java/com/cloud/agent/AgentTest.java | 8 +++---
5 files changed, 67 insertions(+), 6 deletions(-)
diff --git a/agent/src/main/java/com/cloud/agent/Agent.java b/agent/src/main/java/com/cloud/agent/Agent.java
index c01f025c6a8..75dea0cc711 100644
--- a/agent/src/main/java/com/cloud/agent/Agent.java
+++ b/agent/src/main/java/com/cloud/agent/Agent.java
@@ -131,7 +131,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater
CopyOnWriteArrayList<IAgentControlListener> controlListeners = new CopyOnWriteArrayList<>();
IAgentShell shell;
- NioConnection connection;
+ NioClient connection;
ServerResource serverResource;
Link link;
Long id;
@@ -919,6 +919,20 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater
}
}
+ /**
+ * Saves the currently connected management server host after successful setup completion.
+ * This host is persisted and later added to the reconnection list as a fallback option.
+ * Called after receiving a Ready command from the management server, indicating that
+ * the agent has successfully completed its initialization and is ready to work.
+ *
+ * @param connectedHost the hostname or IP address of the successfully connected management server
+ */
+ private void updateLastSetupCompletedHost(String connectedHost) {
+ if (StringUtils.isNotBlank(connectedHost)) {
+ shell.setLastSetupCompletedHost(connectedHost);
+ }
+ }
+
private Answer setupManagementServerList(final SetupMSListCommand cmd) {
processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval());
return new SetupMSListAnswer(true);
@@ -959,6 +973,8 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater
verifyAgentArch(ready.getArch());
processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval());
+ String connectedHost = shell.getConnectedHost();
+ updateLastSetupCompletedHost(connectedHost);
logger.info("Ready command is processed for agent [id: {}, uuid: {}, name: {}]", getId(), getUuid(), getName());
}
diff --git a/agent/src/main/java/com/cloud/agent/AgentShell.java b/agent/src/main/java/com/cloud/agent/AgentShell.java
index c5257b95b7c..2898a16bd2a 100644
--- a/agent/src/main/java/com/cloud/agent/AgentShell.java
+++ b/agent/src/main/java/com/cloud/agent/AgentShell.java
@@ -154,7 +154,20 @@ public class AgentShell implements IAgentShell, Daemon {
@Override
public String[] getHosts() {
- return _host.split(",");
+ String lastSetupCompletedHost = getLastSetupCompletedHost();
+ String host;
+ // Add the last successful setup host as a fallback option at the end of the host list.
+ // This host is tried only after all configured hosts have failed, providing a
+ // last-resort connection option since this host previously completed setup successfully.
+ if (StringUtils.isNotBlank(lastSetupCompletedHost)
+ && StringUtils.isNotBlank(_host)
+ && !_host.contains(lastSetupCompletedHost)) {
+ host = _host + "," + lastSetupCompletedHost;
+ } else {
+ host = _host;
+ }
+
+ return host.split(",");
}
@Override
@@ -464,6 +477,21 @@ public class AgentShell implements IAgentShell, Daemon {
return AgentPropertiesFileHandler.getPropertyValue(AgentProperties.SSL_HANDSHAKE_TIMEOUT);
}
+ @Override
+ public void setLastSetupCompletedHost(String host) {
+ setPersistentProperty(null, AgentProperties.LAST_SETUP_COMPLETED_HOST.getName(), host);
+ }
+
+ /**
+ * Gets the last host where the agent successfully completed its setup process
+ * and received a Ready command.
+ *
+ * @return the hostname or IP address of the last successfully setup host, or null if none exists
+ */
+ private String getLastSetupCompletedHost() {
+ return AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LAST_SETUP_COMPLETED_HOST);
+ }
+
public synchronized int getNextAgentId() {
return _nextAgentId++;
}
diff --git a/agent/src/main/java/com/cloud/agent/IAgentShell.java b/agent/src/main/java/com/cloud/agent/IAgentShell.java
index 7f04048795d..c5c94c7e51d 100644
--- a/agent/src/main/java/com/cloud/agent/IAgentShell.java
+++ b/agent/src/main/java/com/cloud/agent/IAgentShell.java
@@ -72,4 +72,13 @@ public interface IAgentShell {
void launchNewAgent(ServerResource resource) throws ConfigurationException;
Integer getSslHandshakeTimeout();
+
+ /**
+ * Sets the last host where the agent successfully completed its setup process
+ * and received a Ready command. This value is persisted across agent restarts
+ * and used to prioritize reconnection attempts to previously working hosts.
+ *
+ * @param host the hostname or IP address where the agent setup completed successfully
+ */
+ void setLastSetupCompletedHost(String host);
}
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
index c781c07c227..bf88564901a 100644
--- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -57,6 +57,14 @@ public class AgentProperties{
*/
public static final Property<String> HOST = new Property<>("host", "localhost");
+ /**
+ * The name of the last host where the agent successfully completed its setup process
+ * and received a Ready command
+ * Data type: String.<br>
+ * Default value: <code>null</code>
+ */
+ public static final Property<String> LAST_SETUP_COMPLETED_HOST = new Property<>("last.setup.completed.host", null, String.class);
+
/**
* The time interval (in seconds) after which the agent will check if the connected host is the preferred host.<br>
* After that interval, if the agent is connected to one of the secondary/backup hosts, it will attempt to reconnect to the preferred host.<br>
diff --git a/agent/src/test/java/com/cloud/agent/AgentTest.java b/agent/src/test/java/com/cloud/agent/AgentTest.java
index 65dc030ebd7..0dda32481e7 100644
--- a/agent/src/test/java/com/cloud/agent/AgentTest.java
+++ b/agent/src/test/java/com/cloud/agent/AgentTest.java
@@ -36,6 +36,7 @@ import java.net.InetSocketAddress;
import javax.naming.ConfigurationException;
import org.apache.logging.log4j.Logger;
+import com.cloud.utils.nio.NioClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +46,6 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.resource.ServerResource;
import com.cloud.utils.backoff.impl.ConstantTimeBackoff;
import com.cloud.utils.nio.Link;
-import com.cloud.utils.nio.NioConnection;
@RunWith(MockitoJUnitRunner.class)
public class AgentTest {
@@ -224,7 +224,7 @@ public class AgentTest {
@Test
public void testStopAndCleanupConnectionValidConnectionNoWaitStopsAndCleansUp() throws IOException {
- NioConnection mockConnection = mock(NioConnection.class);
+ NioClient mockConnection = mock(NioClient.class);
agent.connection = mockConnection;
agent.stopAndCleanupConnection(false);
verify(mockConnection).stop();
@@ -233,7 +233,7 @@ public class AgentTest {
@Test
public void testStopAndCleanupConnectionCleanupThrowsIOExceptionLogsWarning() throws IOException {
- NioConnection mockConnection = mock(NioConnection.class);
+ NioClient mockConnection = mock(NioClient.class);
agent.connection = mockConnection;
doThrow(new IOException("Cleanup failed")).when(mockConnection).cleanUp();
agent.stopAndCleanupConnection(false);
@@ -243,7 +243,7 @@ public class AgentTest {
@Test
public void testStopAndCleanupConnectionValidConnectionWaitForStopWaitsForStartupToStop() throws IOException {
- NioConnection mockConnection = mock(NioConnection.class);
+ NioClient mockConnection = mock(NioClient.class);
ConstantTimeBackoff mockBackoff = mock(ConstantTimeBackoff.class);
mockBackoff.setTimeToWait(0);
agent.connection = mockConnection;