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) {