Skip to content

Commit 9dd93ce

Browse files
Support for custom SSH port for KVM hosts from the host url on add host and the configuration (#12571)
1 parent 8c12a13 commit 9dd93ce

File tree

10 files changed

+94
-10
lines changed

10 files changed

+94
-10
lines changed

api/src/main/java/com/cloud/host/Host.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ public static String[] toStrings(Host.Type... types) {
5959
String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
6060
String HOST_OVFTOOL_VERSION = "host.ovftool.version";
6161
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
62+
String HOST_SSH_PORT = "host.ssh.port";
63+
64+
int DEFAULT_SSH_PORT = 22;
6265

6366
/**
6467
* @return name of the machine.

api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public class AddHostCmd extends BaseCmd {
6060
@Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, required = true, description = "The Pod ID for the host")
6161
private Long podId;
6262

63-
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The host URL")
63+
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The host URL, optionally add ssh port (format: 'host:port') for KVM hosts," +
64+
" otherwise falls back to the port defined at the config 'kvm.host.discovery.ssh.port'")
6465
private String url;
6566

6667
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The Zone ID for the host")

engine/components-api/src/main/java/com/cloud/agent/AgentManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public interface AgentManager {
5454
"This timeout overrides the wait global config. This holds a comma separated key value pairs containing timeout (in seconds) for specific commands. " +
5555
"For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false);
5656

57+
ConfigKey<Integer> KVMHostDiscoverySshPort = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class,
58+
"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)." +
59+
" Please note that this is applicable when port is not defined through host url while adding the KVM host.", true, ConfigKey.Scope.Cluster);
60+
5761
enum TapAgentsAction {
5862
Add, Del, Contains,
5963
}
@@ -170,4 +174,6 @@ enum TapAgentsAction {
170174
void notifyMonitorsOfRemovedHost(long hostId, long clusterId);
171175

172176
void propagateChangeToAgents(Map<String, String> params);
177+
178+
int getHostSshPort(HostVO host);
173179
}

engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import javax.inject.Inject;
4141
import javax.naming.ConfigurationException;
4242

43+
import com.cloud.utils.StringUtils;
4344
import org.apache.cloudstack.agent.lb.IndirectAgentLB;
4445
import org.apache.cloudstack.ca.CAManager;
4546
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -55,7 +56,6 @@
5556
import org.apache.commons.collections.MapUtils;
5657
import org.apache.commons.lang3.BooleanUtils;
5758
import org.apache.commons.lang3.ObjectUtils;
58-
import org.apache.commons.lang3.StringUtils;
5959
import org.apache.logging.log4j.ThreadContext;
6060

6161
import com.cloud.agent.AgentManager;
@@ -1977,7 +1977,7 @@ public ConfigKey<?>[] getConfigKeys() {
19771977
return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize,
19781978
DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait,
19791979
GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections,
1980-
RemoteAgentNewConnectionsMonitorInterval };
1980+
RemoteAgentNewConnectionsMonitorInterval, KVMHostDiscoverySshPort };
19811981
}
19821982

19831983
protected class SetHostParamsListener implements Listener {
@@ -2093,6 +2093,25 @@ public void propagateChangeToAgents(Map<String, String> params) {
20932093
}
20942094
}
20952095

2096+
@Override
2097+
public int getHostSshPort(HostVO host) {
2098+
if (host == null) {
2099+
return KVMHostDiscoverySshPort.value();
2100+
}
2101+
2102+
if (host.getHypervisorType() != HypervisorType.KVM) {
2103+
return Host.DEFAULT_SSH_PORT;
2104+
}
2105+
2106+
_hostDao.loadDetails(host);
2107+
String hostPort = host.getDetail(Host.HOST_SSH_PORT);
2108+
if (StringUtils.isBlank(hostPort)) {
2109+
return KVMHostDiscoverySshPort.valueIn(host.getClusterId());
2110+
}
2111+
2112+
return Integer.parseInt(hostPort);
2113+
}
2114+
20962115
private GlobalLock getHostJoinLock(Long hostId) {
20972116
return GlobalLock.getInternLock(String.format("%s-%s", "Host-Join", hostId));
20982117
}

engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import com.cloud.agent.api.StartupCommand;
2323
import com.cloud.agent.api.StartupRoutingCommand;
2424
import com.cloud.exception.ConnectionException;
25+
import com.cloud.host.Host;
2526
import com.cloud.host.HostVO;
2627
import com.cloud.host.Status;
2728
import com.cloud.host.dao.HostDao;
29+
import com.cloud.hypervisor.Hypervisor;
2830
import com.cloud.utils.Pair;
2931
import org.junit.Assert;
3032
import org.junit.Before;
@@ -103,4 +105,36 @@ public void testGetTimeoutWithGranularTimeout() {
103105

104106
Assert.assertEquals(50, result);
105107
}
108+
109+
@Test
110+
public void testGetHostSshPortWithHostNull() {
111+
int hostSshPort = mgr.getHostSshPort(null);
112+
Assert.assertEquals(22, hostSshPort);
113+
}
114+
115+
@Test
116+
public void testGetHostSshPortWithNonKVMHost() {
117+
HostVO host = Mockito.mock(HostVO.class);
118+
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer);
119+
int hostSshPort = mgr.getHostSshPort(host);
120+
Assert.assertEquals(22, hostSshPort);
121+
}
122+
123+
@Test
124+
public void testGetHostSshPortWithKVMHostDefaultPort() {
125+
HostVO host = Mockito.mock(HostVO.class);
126+
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
127+
Mockito.when(host.getClusterId()).thenReturn(1L);
128+
int hostSshPort = mgr.getHostSshPort(host);
129+
Assert.assertEquals(22, hostSshPort);
130+
}
131+
132+
@Test
133+
public void testGetHostSshPortWithKVMHostCustomPort() {
134+
HostVO host = Mockito.mock(HostVO.class);
135+
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
136+
Mockito.when(host.getDetail(Host.HOST_SSH_PORT)).thenReturn(String.valueOf(3922));
137+
int hostSshPort = mgr.getHostSshPort(host);
138+
Assert.assertEquals(3922, hostSshPort);
139+
}
106140
}

plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.backup;
1818

19+
import com.cloud.agent.AgentManager;
1920
import com.cloud.dc.dao.ClusterDao;
2021
import com.cloud.host.HostVO;
2122
import com.cloud.host.Status;
@@ -117,6 +118,9 @@ public class NetworkerBackupProvider extends AdapterBase implements BackupProvid
117118
@Inject
118119
private VMInstanceDao vmInstanceDao;
119120

121+
@Inject
122+
private AgentManager agentMgr;
123+
120124
private static String getUrlDomain(String url) throws URISyntaxException {
121125
URI uri;
122126
try {
@@ -229,8 +233,13 @@ private String executeBackupCommand(HostVO host, String username, String passwor
229233
String nstRegex = "\\bcompleted savetime=([0-9]{10})";
230234
Pattern saveTimePattern = Pattern.compile(nstRegex);
231235

236+
if (host == null) {
237+
LOG.warn("Unable to take backup, host is null");
238+
return null;
239+
}
240+
232241
try {
233-
Pair<Boolean, String> response = SshHelper.sshExecute(host.getPrivateIpAddress(), 22,
242+
Pair<Boolean, String> response = SshHelper.sshExecute(host.getPrivateIpAddress(), agentMgr.getHostSshPort(host),
234243
username, null, password, command, 120000, 120000, 3600000);
235244
if (!response.first()) {
236245
LOG.error(String.format("Backup Script failed on HYPERVISOR %s due to: %s", host, response.second()));
@@ -249,9 +258,13 @@ private String executeBackupCommand(HostVO host, String username, String passwor
249258
return null;
250259
}
251260
private boolean executeRestoreCommand(HostVO host, String username, String password, String command) {
261+
if (host == null) {
262+
LOG.warn("Unable to restore backup, host is null");
263+
return false;
264+
}
252265

253266
try {
254-
Pair<Boolean, String> response = SshHelper.sshExecute(host.getPrivateIpAddress(), 22,
267+
Pair<Boolean, String> response = SshHelper.sshExecute(host.getPrivateIpAddress(), agentMgr.getHostSshPort(host),
255268
username, null, password, command, 120000, 120000, 3600000);
256269

257270
if (!response.first()) {

server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,12 @@ private void setupAgentSecurity(final Connection sshConnection, final String age
272272
}
273273
}
274274

275-
sshConnection = new Connection(agentIp, 22);
275+
int port = uri.getPort();
276+
if (port <= 0) {
277+
port = AgentManager.KVMHostDiscoverySshPort.valueIn(clusterId);
278+
}
279+
280+
sshConnection = new Connection(agentIp, port);
276281

277282
sshConnection.connect(null, 60000, 60000);
278283

@@ -380,6 +385,9 @@ private void setupAgentSecurity(final Connection sshConnection, final String age
380385
Map<String, String> hostDetails = connectedHost.getDetails();
381386
hostDetails.put("password", password);
382387
hostDetails.put("username", username);
388+
if (uri.getPort() > 0) {
389+
hostDetails.put(Host.HOST_SSH_PORT, String.valueOf(uri.getPort()));
390+
}
383391
_hostDao.saveDetails(connectedHost);
384392
return resources;
385393
} catch (DiscoveredWithErrorException e) {

server/src/main/java/com/cloud/resource/ResourceManagerImpl.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ private List<HostVO> discoverHostsFull(final Long dcId, final Long podId, Long c
776776
_clusterDetailsDao.persist(cluster_cpu_detail);
777777
_clusterDetailsDao.persist(cluster_memory_detail);
778778
}
779-
780779
}
781780

782781
try {
@@ -871,7 +870,6 @@ private List<HostVO> discoverHostsFull(final Long dcId, final Long podId, Long c
871870
hosts.add(host);
872871
}
873872
discoverer.postDiscovery(hosts, _nodeId);
874-
875873
}
876874
logger.info("server resources successfully discovered by " + discoverer.getName());
877875
return hosts;
@@ -2960,7 +2958,7 @@ protected Ternary<String, String, String> getHostCredentials(HostVO host) {
29602958
*/
29612959
protected void connectAndRestartAgentOnHost(HostVO host, String username, String password, String privateKey) {
29622960
final com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection(
2963-
host.getPrivateIpAddress(), 22, username, password, privateKey);
2961+
host.getPrivateIpAddress(), _agentMgr.getHostSshPort(host), username, password, privateKey);
29642962
if (connection == null) {
29652963
throw new CloudRuntimeException(String.format("SSH to agent is enabled, but failed to connect to %s via IP address [%s].", host, host.getPrivateIpAddress()));
29662964
}

server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,14 @@ public void testConnectAndRestartAgentOnHostCannotRestart() throws Exception {
360360

361361
@Test
362362
public void testConnectAndRestartAgentOnHost() {
363+
when(agentManager.getHostSshPort(any())).thenReturn(22);
363364
resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword, hostPrivateKey);
364365
}
365366

366367
@Test
367368
public void testHandleAgentSSHEnabledNotConnectedAgent() {
368369
when(host.getStatus()).thenReturn(Status.Disconnected);
370+
when(agentManager.getHostSshPort(any())).thenReturn(22);
369371
resourceManager.handleAgentIfNotConnected(host, false);
370372
verify(resourceManager).getHostCredentials(eq(host));
371373
verify(resourceManager).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));

utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public static com.trilead.ssh2.Connection acquireAuthorizedConnection(String ip,
7777
}
7878

7979
public static com.trilead.ssh2.Connection acquireAuthorizedConnection(String ip, int port, String username, String password) {
80-
return acquireAuthorizedConnection(ip, 22, username, password, null);
80+
return acquireAuthorizedConnection(ip, port, username, password, null);
8181
}
8282

8383
public static boolean acquireAuthorizedConnectionWithPublicKey(final com.trilead.ssh2.Connection sshConnection, final String username, final String privateKey) {

0 commit comments

Comments
 (0)