This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.20 by this push:
     new 9dd93cef760 Support for custom SSH port for KVM hosts from the host 
url on add host and the configuration (#12571)
9dd93cef760 is described below

commit 9dd93cef76056833c94487fb39c8e8997e2e03b0
Author: Suresh Kumar Anaparti <[email protected]>
AuthorDate: Thu Feb 19 00:35:51 2026 +0530

    Support for custom SSH port for KVM hosts from the host url on add host and 
the configuration (#12571)
---
 api/src/main/java/com/cloud/host/Host.java         |  3 ++
 .../api/command/admin/host/AddHostCmd.java         |  3 +-
 .../main/java/com/cloud/agent/AgentManager.java    |  6 ++++
 .../com/cloud/agent/manager/AgentManagerImpl.java  | 23 +++++++++++++--
 .../cloud/agent/manager/AgentManagerImplTest.java  | 34 ++++++++++++++++++++++
 .../cloudstack/backup/NetworkerBackupProvider.java | 17 +++++++++--
 .../kvm/discoverer/LibvirtServerDiscoverer.java    | 10 ++++++-
 .../com/cloud/resource/ResourceManagerImpl.java    |  4 +--
 .../cloud/resource/ResourceManagerImplTest.java    |  2 ++
 .../java/com/cloud/utils/ssh/SSHCmdHelper.java     |  2 +-
 10 files changed, 94 insertions(+), 10 deletions(-)

diff --git a/api/src/main/java/com/cloud/host/Host.java 
b/api/src/main/java/com/cloud/host/Host.java
index a3b6ccadc01..4672b302776 100644
--- a/api/src/main/java/com/cloud/host/Host.java
+++ b/api/src/main/java/com/cloud/host/Host.java
@@ -59,6 +59,9 @@ public interface Host extends StateObject<Status>, Identity, 
Partition, HAResour
     String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
     String HOST_OVFTOOL_VERSION = "host.ovftool.version";
     String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
+    String HOST_SSH_PORT = "host.ssh.port";
+
+    int DEFAULT_SSH_PORT = 22;
 
     /**
      * @return name of the machine.
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java
index 6c8eded2618..d91828c89db 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java
@@ -60,7 +60,8 @@ public class AddHostCmd extends BaseCmd {
     @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType 
= PodResponse.class, required = true, description = "The Pod ID for the host")
     private Long podId;
 
-    @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = 
true, description = "The host URL")
+    @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = 
true, description = "The host URL, optionally add ssh port (format: 
'host:port') for KVM hosts," +
+            " otherwise falls back to the port defined at the config 
'kvm.host.discovery.ssh.port'")
     private String url;
 
     @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, 
entityType = ZoneResponse.class, required = true, description = "The Zone ID 
for the host")
diff --git 
a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java 
b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
index b29eb38395f..f70ab494fdc 100644
--- a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
+++ b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
@@ -54,6 +54,10 @@ public interface AgentManager {
             "This timeout overrides the wait global config. This holds a comma 
separated key value pairs containing timeout (in seconds) for specific 
commands. " +
                     "For example: DhcpEntryCommand=600, 
SavePasswordCommand=300, VmDataCommand=300", false);
 
+    ConfigKey<Integer> KVMHostDiscoverySshPort = new 
ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class,
+            "kvm.host.discovery.ssh.port", 
String.valueOf(Host.DEFAULT_SSH_PORT), "SSH port used for KVM host discovery 
and any other operations on host (using SSH)." +
+                " Please note that this is applicable when port is not defined 
through host url while adding the KVM host.", true, ConfigKey.Scope.Cluster);
+
     enum TapAgentsAction {
         Add, Del, Contains,
     }
@@ -170,4 +174,6 @@ public interface AgentManager {
     void notifyMonitorsOfRemovedHost(long hostId, long clusterId);
 
     void propagateChangeToAgents(Map<String, String> params);
+
+    int getHostSshPort(HostVO host);
 }
diff --git 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
index 2b5e81eb3f9..ebe0465e3f0 100644
--- 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
@@ -40,6 +40,7 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.utils.StringUtils;
 import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.ca.CAManager;
 import 
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -55,7 +56,6 @@ import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToSt
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.ThreadContext;
 
 import com.cloud.agent.AgentManager;
@@ -1977,7 +1977,7 @@ public class AgentManagerImpl extends ManagerBase 
implements AgentManager, Handl
         return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, 
Wait, AlertWait, DirectAgentLoadSize,
                 DirectAgentPoolSize, DirectAgentThreadCap, 
EnableKVMAutoEnableDisable, ReadyCommandWait,
                 GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, 
RemoteAgentMaxConcurrentNewConnections,
-                RemoteAgentNewConnectionsMonitorInterval };
+                RemoteAgentNewConnectionsMonitorInterval, 
KVMHostDiscoverySshPort };
     }
 
     protected class SetHostParamsListener implements Listener {
@@ -2093,6 +2093,25 @@ public class AgentManagerImpl extends ManagerBase 
implements AgentManager, Handl
         }
     }
 
+    @Override
+    public int getHostSshPort(HostVO host) {
+        if (host == null) {
+            return KVMHostDiscoverySshPort.value();
+        }
+
+        if (host.getHypervisorType() != HypervisorType.KVM) {
+            return Host.DEFAULT_SSH_PORT;
+        }
+
+        _hostDao.loadDetails(host);
+        String hostPort = host.getDetail(Host.HOST_SSH_PORT);
+        if (StringUtils.isBlank(hostPort)) {
+            return KVMHostDiscoverySshPort.valueIn(host.getClusterId());
+        }
+
+        return Integer.parseInt(hostPort);
+    }
+
     private GlobalLock getHostJoinLock(Long hostId) {
         return GlobalLock.getInternLock(String.format("%s-%s", "Host-Join", 
hostId));
     }
diff --git 
a/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java
 
b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java
index 52b7ed77533..293a988dd2c 100644
--- 
a/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java
+++ 
b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java
@@ -22,9 +22,11 @@ import com.cloud.agent.api.ReadyCommand;
 import com.cloud.agent.api.StartupCommand;
 import com.cloud.agent.api.StartupRoutingCommand;
 import com.cloud.exception.ConnectionException;
+import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
 import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
 import com.cloud.utils.Pair;
 import org.junit.Assert;
 import org.junit.Before;
@@ -103,4 +105,36 @@ public class AgentManagerImplTest {
 
         Assert.assertEquals(50, result);
     }
+
+    @Test
+    public void testGetHostSshPortWithHostNull() {
+        int hostSshPort = mgr.getHostSshPort(null);
+        Assert.assertEquals(22, hostSshPort);
+    }
+
+    @Test
+    public void testGetHostSshPortWithNonKVMHost() {
+        HostVO host = Mockito.mock(HostVO.class);
+        
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer);
+        int hostSshPort = mgr.getHostSshPort(host);
+        Assert.assertEquals(22, hostSshPort);
+    }
+
+    @Test
+    public void testGetHostSshPortWithKVMHostDefaultPort() {
+        HostVO host = Mockito.mock(HostVO.class);
+        
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
+        Mockito.when(host.getClusterId()).thenReturn(1L);
+        int hostSshPort = mgr.getHostSshPort(host);
+        Assert.assertEquals(22, hostSshPort);
+    }
+
+    @Test
+    public void testGetHostSshPortWithKVMHostCustomPort() {
+        HostVO host = Mockito.mock(HostVO.class);
+        
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
+        
Mockito.when(host.getDetail(Host.HOST_SSH_PORT)).thenReturn(String.valueOf(3922));
+        int hostSshPort = mgr.getHostSshPort(host);
+        Assert.assertEquals(3922, hostSshPort);
+    }
 }
diff --git 
a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java
 
b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java
index 393e2911ac3..3f14ab259a0 100644
--- 
a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java
+++ 
b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.backup;
 
+import com.cloud.agent.AgentManager;
 import com.cloud.dc.dao.ClusterDao;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
@@ -117,6 +118,9 @@ public class NetworkerBackupProvider extends AdapterBase 
implements BackupProvid
     @Inject
     private VMInstanceDao vmInstanceDao;
 
+    @Inject
+    private AgentManager agentMgr;
+
     private static String getUrlDomain(String url) throws URISyntaxException {
         URI uri;
         try {
@@ -229,8 +233,13 @@ public class NetworkerBackupProvider extends AdapterBase 
implements BackupProvid
         String nstRegex = "\\bcompleted savetime=([0-9]{10})";
         Pattern saveTimePattern = Pattern.compile(nstRegex);
 
+        if (host == null) {
+            LOG.warn("Unable to take backup, host is null");
+            return null;
+        }
+
         try {
-            Pair<Boolean, String> response = 
SshHelper.sshExecute(host.getPrivateIpAddress(), 22,
+            Pair<Boolean, String> response = 
SshHelper.sshExecute(host.getPrivateIpAddress(), agentMgr.getHostSshPort(host),
                     username, null, password, command, 120000, 120000, 
3600000);
             if (!response.first()) {
                 LOG.error(String.format("Backup Script failed on HYPERVISOR %s 
due to: %s", host, response.second()));
@@ -249,9 +258,13 @@ public class NetworkerBackupProvider extends AdapterBase 
implements BackupProvid
         return null;
     }
     private boolean executeRestoreCommand(HostVO host, String username, String 
password, String command) {
+        if (host == null) {
+            LOG.warn("Unable to restore backup, host is null");
+            return false;
+        }
 
         try {
-            Pair<Boolean, String> response = 
SshHelper.sshExecute(host.getPrivateIpAddress(), 22,
+            Pair<Boolean, String> response = 
SshHelper.sshExecute(host.getPrivateIpAddress(), agentMgr.getHostSshPort(host),
                 username, null, password, command, 120000, 120000, 3600000);
 
             if (!response.first()) {
diff --git 
a/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
 
b/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
index 8f7bf21dff2..da3ca47ae9c 100644
--- 
a/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
+++ 
b/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
@@ -272,7 +272,12 @@ public abstract class LibvirtServerDiscoverer extends 
DiscovererBase implements
                 }
             }
 
-            sshConnection = new Connection(agentIp, 22);
+            int port = uri.getPort();
+            if (port <= 0) {
+                port = AgentManager.KVMHostDiscoverySshPort.valueIn(clusterId);
+            }
+
+            sshConnection = new Connection(agentIp, port);
 
             sshConnection.connect(null, 60000, 60000);
 
@@ -380,6 +385,9 @@ public abstract class LibvirtServerDiscoverer extends 
DiscovererBase implements
             Map<String, String> hostDetails = connectedHost.getDetails();
             hostDetails.put("password", password);
             hostDetails.put("username", username);
+            if (uri.getPort() > 0) {
+                hostDetails.put(Host.HOST_SSH_PORT, 
String.valueOf(uri.getPort()));
+            }
             _hostDao.saveDetails(connectedHost);
             return resources;
         } catch (DiscoveredWithErrorException e) {
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java 
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 96331477e89..cc789bf5650 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -776,7 +776,6 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
                 _clusterDetailsDao.persist(cluster_cpu_detail);
                 _clusterDetailsDao.persist(cluster_memory_detail);
             }
-
         }
 
         try {
@@ -871,7 +870,6 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
                         hosts.add(host);
                     }
                     discoverer.postDiscovery(hosts, _nodeId);
-
                 }
                 logger.info("server resources successfully discovered by " + 
discoverer.getName());
                 return hosts;
@@ -2960,7 +2958,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
      */
     protected void connectAndRestartAgentOnHost(HostVO host, String username, 
String password, String privateKey) {
         final com.trilead.ssh2.Connection connection = 
SSHCmdHelper.acquireAuthorizedConnection(
-                host.getPrivateIpAddress(), 22, username, password, 
privateKey);
+                host.getPrivateIpAddress(), _agentMgr.getHostSshPort(host), 
username, password, privateKey);
         if (connection == null) {
             throw new CloudRuntimeException(String.format("SSH to agent is 
enabled, but failed to connect to %s via IP address [%s].", host, 
host.getPrivateIpAddress()));
         }
diff --git 
a/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java 
b/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
index 1669d7a47d9..91e4bf7a47b 100644
--- a/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
+++ b/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
@@ -360,12 +360,14 @@ public class ResourceManagerImplTest {
 
     @Test
     public void testConnectAndRestartAgentOnHost() {
+        when(agentManager.getHostSshPort(any())).thenReturn(22);
         resourceManager.connectAndRestartAgentOnHost(host, hostUsername, 
hostPassword, hostPrivateKey);
     }
 
     @Test
     public void testHandleAgentSSHEnabledNotConnectedAgent() {
         when(host.getStatus()).thenReturn(Status.Disconnected);
+        when(agentManager.getHostSshPort(any())).thenReturn(22);
         resourceManager.handleAgentIfNotConnected(host, false);
         verify(resourceManager).getHostCredentials(eq(host));
         verify(resourceManager).connectAndRestartAgentOnHost(eq(host), 
eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));
diff --git a/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java 
b/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java
index dd1c17aa3c0..944f63391a9 100644
--- a/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java
+++ b/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java
@@ -77,7 +77,7 @@ public class SSHCmdHelper {
     }
 
     public static com.trilead.ssh2.Connection 
acquireAuthorizedConnection(String ip, int port, String username, String 
password) {
-        return acquireAuthorizedConnection(ip, 22, username, password, null);
+        return acquireAuthorizedConnection(ip, port, username, password, null);
     }
 
     public static boolean acquireAuthorizedConnectionWithPublicKey(final 
com.trilead.ssh2.Connection sshConnection, final String username, final String 
privateKey) {

Reply via email to