From 9692e29bfc8c10daac6b3209b5083daf6cbcad49 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 3 Feb 2026 15:19:35 +0530 Subject: [PATCH 1/6] Support for custom SSH port for KVM hosts using the configuration 'kvm.host.discovery.ssh.port' - Use the custom SSH port for KVM host discovery to connect to the Host during Add Host command - and any other operations on host using SSH --- .../src/main/java/com/cloud/agent/AgentManager.java | 3 +++ .../main/java/com/cloud/agent/manager/AgentManagerImpl.java | 2 +- .../apache/cloudstack/backup/NetworkerBackupProvider.java | 5 +++-- .../hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java | 2 +- .../main/java/com/cloud/resource/ResourceManagerImpl.java | 2 +- utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) 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 b29eb38395fa..3c6d7fb28919 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,9 @@ 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 KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "kvm.host.discovery.ssh.port", "22", "SSH port used for KVM host discovery and any other operations on host (using SSH)", true); + enum TapAgentsAction { Add, Del, Contains, } 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 2b5e81eb3f9b..bfdd2387bc42 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 @@ -1977,7 +1977,7 @@ public ConfigKey[] getConfigKeys() { return new ConfigKey[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait, GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections, - RemoteAgentNewConnectionsMonitorInterval }; + RemoteAgentNewConnectionsMonitorInterval, KVMHostDiscoverySshPort }; } protected class SetHostParamsListener implements Listener { 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 393e2911ac38..89dfc545c506 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; @@ -230,7 +231,7 @@ private String executeBackupCommand(HostVO host, String username, String passwor Pattern saveTimePattern = Pattern.compile(nstRegex); try { - Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), 22, + Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), AgentManager.KVMHostDiscoverySshPort.value(), 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())); @@ -251,7 +252,7 @@ private String executeBackupCommand(HostVO host, String username, String passwor private boolean executeRestoreCommand(HostVO host, String username, String password, String command) { try { - Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), 22, + Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), AgentManager.KVMHostDiscoverySshPort.value(), 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 8f7bf21dff22..67abdb7af561 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,7 @@ private void setupAgentSecurity(final Connection sshConnection, final String age } } - sshConnection = new Connection(agentIp, 22); + sshConnection = new Connection(agentIp, AgentManager.KVMHostDiscoverySshPort.value()); sshConnection.connect(null, 60000, 60000); diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 12ceac213228..c46f341e7911 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -2949,7 +2949,7 @@ protected Ternary getHostCredentials(HostVO host) { */ 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(), AgentManager.KVMHostDiscoverySshPort.value(), 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/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java b/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java index dd1c17aa3c02..944f63391a9a 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 static com.trilead.ssh2.Connection acquireAuthorizedConnection(String ip, } 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) { From 2e49b0c8cb8a861a51df79b74d0d905198817354 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 4 Feb 2026 13:25:21 +0530 Subject: [PATCH 2/6] Update engine/components-api/src/main/java/com/cloud/agent/AgentManager.java --- .../src/main/java/com/cloud/agent/AgentManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3c6d7fb28919..7690dc14d8a1 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 @@ -55,7 +55,7 @@ public interface AgentManager { "For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false); ConfigKey KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, - "kvm.host.discovery.ssh.port", "22", "SSH port used for KVM host discovery and any other operations on host (using SSH)", true); + "kvm.host.discovery.ssh.port", "22", "SSH port used for KVM host discovery and any other operations on host (using SSH). Please note that this is applicable for all the KVM hosts added to this CloudStack deployment, so ensure all hosts are accessible on this port", true); enum TapAgentsAction { Add, Del, Contains, From 5f4bc9151d7912d0103e59b404b546da5e467344 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 10 Feb 2026 12:00:54 +0530 Subject: [PATCH 3/6] Pick discovery/ssh port from host url while adding KVM host (and use that port if passed, other fall back to config 'kvm.host.discovery.ssh.port') --- api/src/main/java/com/cloud/host/Host.java | 1 + .../api/command/admin/host/AddHostCmd.java | 3 +- .../java/com/cloud/agent/AgentManager.java | 3 +- .../backup/NetworkerBackupProvider.java | 32 +++++++++++++++++-- .../discoverer/LibvirtServerDiscoverer.java | 10 +++++- .../cloud/resource/ResourceManagerImpl.java | 21 ++++++++++-- 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index a3b6ccadc01c..7f4b269c652b 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -59,6 +59,7 @@ public static String[] toStrings(Host.Type... types) { String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; String HOST_OVFTOOL_VERSION = "host.ovftool.version"; String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; + String HOST_SSH_POST = "host.ssh.port"; /** * @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 6c8eded26180..f6565e1e36d5 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 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 7690dc14d8a1..24118ebb6cc1 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 @@ -55,7 +55,8 @@ public interface AgentManager { "For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false); ConfigKey KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, - "kvm.host.discovery.ssh.port", "22", "SSH port used for KVM host discovery and any other operations on host (using SSH). Please note that this is applicable for all the KVM hosts added to this CloudStack deployment, so ensure all hosts are accessible on this port", true); + "kvm.host.discovery.ssh.port", "22", "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, 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 89dfc545c506..ff8e73d61c64 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 @@ -18,6 +18,7 @@ import com.cloud.agent.AgentManager; import com.cloud.dc.dao.ClusterDao; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; @@ -28,6 +29,7 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.Transaction; @@ -230,8 +232,13 @@ private String executeBackupCommand(HostVO host, String username, String passwor 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 response = SshHelper.sshExecute(host.getPrivateIpAddress(), AgentManager.KVMHostDiscoverySshPort.value(), + Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), 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())); @@ -250,9 +257,13 @@ private String executeBackupCommand(HostVO host, String username, String passwor 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 response = SshHelper.sshExecute(host.getPrivateIpAddress(), AgentManager.KVMHostDiscoverySshPort.value(), + Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), getHostSshPort(host), username, null, password, command, 120000, 120000, 3600000); if (!response.first()) { @@ -267,6 +278,23 @@ private boolean executeRestoreCommand(HostVO host, String username, String passw return false; } + private int getHostSshPort(HostVO host) { + if (host == null) { + return AgentManager.KVMHostDiscoverySshPort.value(); + } + + hostDao.loadDetails(host); + String hostPort = host.getDetail(Host.HOST_SSH_POST); + int sshPort; + if (StringUtils.isBlank(hostPort)) { + sshPort = AgentManager.KVMHostDiscoverySshPort.valueIn(host.getClusterId()); + } else { + sshPort = Integer.parseInt(hostPort); + } + + return sshPort; + } + private NetworkerClient getClient(final Long zoneId) { try { return new NetworkerClient(NetworkerUrl.valueIn(zoneId), NetworkerUsername.valueIn(zoneId), NetworkerPassword.valueIn(zoneId), 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 67abdb7af561..01e6b612ce36 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 @@ private void setupAgentSecurity(final Connection sshConnection, final String age } } - sshConnection = new Connection(agentIp, AgentManager.KVMHostDiscoverySshPort.value()); + 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 @@ private void setupAgentSecurity(final Connection sshConnection, final String age Map hostDetails = connectedHost.getDetails(); hostDetails.put("password", password); hostDetails.put("username", username); + if (uri.getPort() > 0) { + hostDetails.put(Host.HOST_SSH_POST, 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 c46f341e7911..c6691f755726 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 @@ private List discoverHostsFull(final Long dcId, final Long podId, Long c _clusterDetailsDao.persist(cluster_cpu_detail); _clusterDetailsDao.persist(cluster_memory_detail); } - } try { @@ -871,7 +870,6 @@ private List discoverHostsFull(final Long dcId, final Long podId, Long c hosts.add(host); } discoverer.postDiscovery(hosts, _nodeId); - } logger.info("server resources successfully discovered by " + discoverer.getName()); return hosts; @@ -2949,7 +2947,7 @@ protected Ternary getHostCredentials(HostVO host) { */ protected void connectAndRestartAgentOnHost(HostVO host, String username, String password, String privateKey) { final com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection( - host.getPrivateIpAddress(), AgentManager.KVMHostDiscoverySshPort.value(), username, password, privateKey); + host.getPrivateIpAddress(), 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())); } @@ -2965,6 +2963,23 @@ protected void connectAndRestartAgentOnHost(HostVO host, String username, String } } + private int getHostSshPort(HostVO host) { + if (host == null) { + return AgentManager.KVMHostDiscoverySshPort.value(); + } + + _hostDao.loadDetails(host); + String hostPort = host.getDetail(Host.HOST_SSH_POST); + int sshPort; + if (StringUtils.isBlank(hostPort)) { + sshPort = AgentManager.KVMHostDiscoverySshPort.valueIn(host.getClusterId()); + } else { + sshPort = Integer.parseInt(hostPort); + } + + return sshPort; + } + public boolean cancelMaintenance(final long hostId) { try { final Boolean result = propagateResourceEvent(hostId, ResourceState.Event.AdminCancelMaintenance); From 5347dcefeac481ef638c4c777176f3df9c30738a Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 10 Feb 2026 14:13:55 +0530 Subject: [PATCH 4/6] move method getHostSshPort to AgentManager --- .../java/com/cloud/agent/AgentManager.java | 2 ++ .../cloud/agent/manager/AgentManagerImpl.java | 18 +++++++++++++ .../backup/NetworkerBackupProvider.java | 26 ++++--------------- .../cloud/resource/ResourceManagerImpl.java | 19 +------------- .../resource/ResourceManagerImplTest.java | 2 ++ 5 files changed, 28 insertions(+), 39 deletions(-) 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 24118ebb6cc1..1f662424c488 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 @@ -174,4 +174,6 @@ enum TapAgentsAction { void notifyMonitorsOfRemovedHost(long hostId, long clusterId); void propagateChangeToAgents(Map 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 bfdd2387bc42..bd1749d2b2ed 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 @@ -2093,6 +2093,24 @@ public void propagateChangeToAgents(Map params) { } } + @Override + public int getHostSshPort(HostVO host) { + if (host == null) { + return KVMHostDiscoverySshPort.value(); + } + + _hostDao.loadDetails(host); + String hostPort = host.getDetail(Host.HOST_SSH_POST); + int sshPort; + if (com.cloud.utils.StringUtils.isBlank(hostPort)) { + sshPort = KVMHostDiscoverySshPort.valueIn(host.getClusterId()); + } else { + sshPort = Integer.parseInt(hostPort); + } + + return sshPort; + } + private GlobalLock getHostJoinLock(Long hostId) { return GlobalLock.getInternLock(String.format("%s-%s", "Host-Join", hostId)); } 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 ff8e73d61c64..3f14ab259a06 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 @@ -18,7 +18,6 @@ import com.cloud.agent.AgentManager; import com.cloud.dc.dao.ClusterDao; -import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; @@ -29,7 +28,6 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.Pair; -import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.Transaction; @@ -120,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 { @@ -238,7 +239,7 @@ private String executeBackupCommand(HostVO host, String username, String passwor } try { - Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), getHostSshPort(host), + Pair 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())); @@ -263,7 +264,7 @@ private boolean executeRestoreCommand(HostVO host, String username, String passw } try { - Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), getHostSshPort(host), + Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), agentMgr.getHostSshPort(host), username, null, password, command, 120000, 120000, 3600000); if (!response.first()) { @@ -278,23 +279,6 @@ private boolean executeRestoreCommand(HostVO host, String username, String passw return false; } - private int getHostSshPort(HostVO host) { - if (host == null) { - return AgentManager.KVMHostDiscoverySshPort.value(); - } - - hostDao.loadDetails(host); - String hostPort = host.getDetail(Host.HOST_SSH_POST); - int sshPort; - if (StringUtils.isBlank(hostPort)) { - sshPort = AgentManager.KVMHostDiscoverySshPort.valueIn(host.getClusterId()); - } else { - sshPort = Integer.parseInt(hostPort); - } - - return sshPort; - } - private NetworkerClient getClient(final Long zoneId) { try { return new NetworkerClient(NetworkerUrl.valueIn(zoneId), NetworkerUsername.valueIn(zoneId), NetworkerPassword.valueIn(zoneId), diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index c6691f755726..f9d12819aebb 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -2947,7 +2947,7 @@ protected Ternary getHostCredentials(HostVO host) { */ protected void connectAndRestartAgentOnHost(HostVO host, String username, String password, String privateKey) { final com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection( - host.getPrivateIpAddress(), getHostSshPort(host), 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())); } @@ -2963,23 +2963,6 @@ protected void connectAndRestartAgentOnHost(HostVO host, String username, String } } - private int getHostSshPort(HostVO host) { - if (host == null) { - return AgentManager.KVMHostDiscoverySshPort.value(); - } - - _hostDao.loadDetails(host); - String hostPort = host.getDetail(Host.HOST_SSH_POST); - int sshPort; - if (StringUtils.isBlank(hostPort)) { - sshPort = AgentManager.KVMHostDiscoverySshPort.valueIn(host.getClusterId()); - } else { - sshPort = Integer.parseInt(hostPort); - } - - return sshPort; - } - public boolean cancelMaintenance(final long hostId) { try { final Boolean result = propagateResourceEvent(hostId, ResourceState.Event.AdminCancelMaintenance); diff --git a/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java b/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java index 1669d7a47d9f..91e4bf7a47b8 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 void testConnectAndRestartAgentOnHostCannotRestart() throws Exception { @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)); From fa48e901a7c0f28f76a2d3f9975128b63b8c65c7 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 10 Feb 2026 14:26:15 +0530 Subject: [PATCH 5/6] review changes --- .../main/java/com/cloud/agent/manager/AgentManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 bd1749d2b2ed..73dc099bf019 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 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.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; @@ -2102,7 +2102,7 @@ public int getHostSshPort(HostVO host) { _hostDao.loadDetails(host); String hostPort = host.getDetail(Host.HOST_SSH_POST); int sshPort; - if (com.cloud.utils.StringUtils.isBlank(hostPort)) { + if (StringUtils.isBlank(hostPort)) { sshPort = KVMHostDiscoverySshPort.valueIn(host.getClusterId()); } else { sshPort = Integer.parseInt(hostPort); From 9308f02badffa92470ced709a9dd66c9d954e7d8 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 11 Feb 2026 18:13:17 +0530 Subject: [PATCH 6/6] review comments --- api/src/main/java/com/cloud/host/Host.java | 2 ++ .../apache/cloudstack/api/command/admin/host/AddHostCmd.java | 2 +- .../src/main/java/com/cloud/agent/AgentManager.java | 2 +- .../main/java/com/cloud/agent/manager/AgentManagerImpl.java | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 7f4b269c652b..99c3e48a7b47 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -61,6 +61,8 @@ public static String[] toStrings(Host.Type... types) { String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; String HOST_SSH_POST = "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 f6565e1e36d5..d91828c89dbf 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,7 @@ 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, optionally add ssh port for KVM hosts," + + @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; 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 1f662424c488..f70ab494fdc1 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 @@ -55,7 +55,7 @@ public interface AgentManager { "For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false); ConfigKey KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, - "kvm.host.discovery.ssh.port", "22", "SSH port used for KVM host discovery and any other operations on host (using SSH)." + + "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 { 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 73dc099bf019..ea62cb7d39ca 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 @@ -2099,6 +2099,10 @@ public int getHostSshPort(HostVO host) { return KVMHostDiscoverySshPort.value(); } + if (host.getHypervisorType() != HypervisorType.KVM) { + return Host.DEFAULT_SSH_PORT; + } + _hostDao.loadDetails(host); String hostPort = host.getDetail(Host.HOST_SSH_POST); int sshPort;