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 <[email protected]>
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;

Reply via email to