Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PendingReleaseNotes
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ example.ver.1 > example.ver.2:
which can now be attached to Instances. This is to prevent the Secondary
Storage to grow to enormous sizes as Linux Distributions keep growing in
size while a stripped down Linux should fit on a 2.88MB floppy.

KVM console:
* Added the global setting `consoleproxy.kvm.multiple.viewers.enabled` to allow
multiple users to simultaneously access the console of the same KVM user VM.
The setting defaults to false to preserve the existing single-viewer behavior.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public interface ConsoleAccessManager extends Manager, Configurable {
false,
ConfigKey.Scope.Global);

ConfigKey<Boolean> KvmMultipleConsoleViewersEnabled = new ConfigKey<>("Advanced", Boolean.class,
"consoleproxy.kvm.multiple.viewers.enabled",
"false",
"Determines whether multiple simultaneous console viewers are allowed for the same KVM user VM",
true,
ConfigKey.Scope.Global);

ConsoleEndpoint generateConsoleEndpoint(Long vmId, String extraSecurityToken, String clientAddress);

boolean isSessionAllowed(String sessionUuid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ public String getConfigComponentName() {
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey[] {
ConsoleAccessManager.ConsoleSessionCleanupInterval,
ConsoleAccessManager.ConsoleSessionCleanupRetentionHours
ConsoleAccessManager.ConsoleSessionCleanupRetentionHours,
ConsoleAccessManager.KvmMultipleConsoleViewersEnabled
};
}

Expand Down Expand Up @@ -474,6 +475,10 @@ protected Pair<String, Integer> getHostAndPortForKVMMaintenanceHostIfNeeded(Host
return null;
}

protected boolean isKvmMultipleConsoleViewersEnabled() {
return ConsoleAccessManager.KvmMultipleConsoleViewersEnabled.value();
}

protected ConsoleConnectionDetails getConsoleConnectionDetails(VirtualMachine vm, HostVO host) {
String locale = null;
String tag = vm.getUuid();
Expand Down Expand Up @@ -507,6 +512,13 @@ protected ConsoleConnectionDetails getConsoleConnectionDetails(VirtualMachine vm
.getValue()));
logger.debug("HyperV RDP port for {} on {} is: {}", vm, host, details.getPort());
}

if (Hypervisor.HypervisorType.KVM.equals(host.getHypervisorType()) &&
VirtualMachine.Type.User.equals(vm.getType()) &&
isKvmMultipleConsoleViewersEnabled()) {
details.setSessionRequiresNewViewer(true);
}

return details;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,80 @@ public void getConsoleConnectionDetailsReturnsDetailsForKVMHypervisor() {
Assert.assertEquals(port, result.getPort());
}

@Test
public void getConsoleConnectionDetailsRequiresNewViewerForKVMUserVmWhenMultipleViewersEnabled() {
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
HostVO host = Mockito.mock(HostVO.class);
String hostAddress = "192.168.1.100";
int port = 5900;
Pair<String, Integer> hostPortInfo = new Pair<>(hostAddress, port);

Mockito.when(vm.getUuid()).thenReturn("vm-uuid");
Mockito.when(vm.getHostName()).thenReturn("vm-hostname");
Mockito.when(vm.getVncPassword()).thenReturn("vnc-password");
Mockito.when(vm.getType()).thenReturn(VirtualMachine.Type.User);
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
Mockito.when(vmInstanceDetailsDao.listDetailsKeyPairs(Mockito.anyLong(), Mockito.anyList())).thenReturn(Map.of());
Mockito.when(managementServer.getVncPort(vm)).thenReturn(hostPortInfo);
Mockito.doReturn(new Ternary<>(hostAddress, null, null))
.when(consoleAccessManager).parseHostInfo(Mockito.anyString());
Mockito.doReturn(true).when(consoleAccessManager).isKvmMultipleConsoleViewersEnabled();

ConsoleConnectionDetails result = consoleAccessManager.getConsoleConnectionDetails(vm, host);

Assert.assertNotNull(result);
Assert.assertTrue(result.isSessionRequiresNewViewer());
}

@Test
public void getConsoleConnectionDetailsDoesNotRequireNewViewerForKVMUserVmWhenMultipleViewersDisabled() {
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
HostVO host = Mockito.mock(HostVO.class);
String hostAddress = "192.168.1.100";
int port = 5900;
Pair<String, Integer> hostPortInfo = new Pair<>(hostAddress, port);

Mockito.when(vm.getUuid()).thenReturn("vm-uuid");
Mockito.when(vm.getHostName()).thenReturn("vm-hostname");
Mockito.when(vm.getVncPassword()).thenReturn("vnc-password");
Mockito.when(vm.getType()).thenReturn(VirtualMachine.Type.User);
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
Mockito.when(vmInstanceDetailsDao.listDetailsKeyPairs(Mockito.anyLong(), Mockito.anyList())).thenReturn(Map.of());
Mockito.when(managementServer.getVncPort(vm)).thenReturn(hostPortInfo);
Mockito.doReturn(new Ternary<>(hostAddress, null, null))
.when(consoleAccessManager).parseHostInfo(Mockito.anyString());
Mockito.doReturn(false).when(consoleAccessManager).isKvmMultipleConsoleViewersEnabled();

ConsoleConnectionDetails result = consoleAccessManager.getConsoleConnectionDetails(vm, host);

Assert.assertNotNull(result);
Assert.assertFalse(result.isSessionRequiresNewViewer());
}

@Test
public void getConsoleConnectionDetailsDoesNotRequireNewViewerForKVMSystemVm() {
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
HostVO host = Mockito.mock(HostVO.class);
String hostAddress = "192.168.1.100";
int port = 5900;
Pair<String, Integer> hostPortInfo = new Pair<>(hostAddress, port);

Mockito.when(vm.getUuid()).thenReturn("vm-uuid");
Mockito.when(vm.getHostName()).thenReturn("vm-hostname");
Mockito.when(vm.getVncPassword()).thenReturn("vnc-password");
Mockito.when(vm.getType()).thenReturn(VirtualMachine.Type.DomainRouter);
Mockito.when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
Mockito.when(vmInstanceDetailsDao.listDetailsKeyPairs(Mockito.anyLong(), Mockito.anyList())).thenReturn(Map.of());
Mockito.when(managementServer.getVncPort(vm)).thenReturn(hostPortInfo);
Mockito.doReturn(new Ternary<>(hostAddress, null, null))
.when(consoleAccessManager).parseHostInfo(Mockito.anyString());
ConsoleConnectionDetails result = consoleAccessManager.getConsoleConnectionDetails(vm, host);

Assert.assertNotNull(result);
Assert.assertFalse(result.isSessionRequiresNewViewer());
Mockito.verify(consoleAccessManager, Mockito.never()).isKvmMultipleConsoleViewersEnabled();
}

@Test
public void getConsoleConnectionDetailsReturnsDetailsWithRDPForHyperV() {
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
Expand Down