diff --git a/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java b/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java index 73ff79301fb7..630a43cbc6a8 100644 --- a/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java @@ -325,6 +325,7 @@ private boolean provisionKvmHostViaSsh(Host host, String caProvider) { provisionCertificateViaSsh(sshConnection, hostIp, host.getName(), caProvider); String sudoPrefix = "root".equals(username) ? "" : "sudo "; + reloadVncTlsCertificateOnRunningVmsViaSsh(sshConnection, sudoPrefix, hostIp); SSHCmdHelper.sshExecuteCmd(sshConnection, sudoPrefix + "systemctl restart libvirtd"); SSHCmdHelper.sshExecuteCmd(sshConnection, sudoPrefix + "systemctl restart cloudstack-agent"); @@ -339,6 +340,22 @@ private boolean provisionKvmHostViaSsh(Host host, String caProvider) { } } + /** + * Live-reloads the VNC TLS certificate on every running VM via SSH, since a libvirtd/cloudstack-agent restart + * alone does not affect VMs already running. Per-VM failures are tolerated and logged, not thrown. + */ + private void reloadVncTlsCertificateOnRunningVmsViaSsh(final Connection sshConnection, final String sudoPrefix, final String hostIp) { + final String cmd = sudoPrefix + "virsh -c qemu:///system list --name --state-running | while read -r vm; do " + + "[ -z \"$vm\" ] && continue; " + + sudoPrefix + "virsh -c qemu:///system qemu-monitor-command \"$vm\" " + + "'{\"execute\":\"display-reload\",\"arguments\":{\"type\":\"vnc\",\"tls-certs\":true}}' >/dev/null 2>&1 " + + "|| echo \"failed to reload VNC TLS certificate for VM $vm\" >&2; done"; + final SSHCmdHelper.SSHCmdResult result = SSHCmdHelper.sshExecuteCmdWithResult(sshConnection, cmd); + if (!result.isSuccess()) { + logger.warn("Failed to reload VNC TLS certificate on running VMs via SSH on host: {}, error: {}", hostIp, result.getStdErr()); + } + } + private boolean provisionSystemVmViaSsh(Host host, Boolean reconnect, String caProvider) { VMInstanceVO vm = vmInstanceDao.findVMByInstanceName(host.getName()); if (vm == null) { diff --git a/server/src/test/java/org/apache/cloudstack/ca/CAManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/ca/CAManagerImplTest.java index 2d60833d35c7..8dceba56c62e 100644 --- a/server/src/test/java/org/apache/cloudstack/ca/CAManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/ca/CAManagerImplTest.java @@ -281,6 +281,10 @@ public void testProvisionKvmHostViaSsh() throws Exception { MockedStatic sshCmdHelperMock = Mockito.mockStatic(SSHCmdHelper.class)) { sshCmdHelperMock.when(() -> SSHCmdHelper.acquireAuthorizedConnectionWithPublicKey(Mockito.any(Connection.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(true); + sshCmdHelperMock.when(() -> SSHCmdHelper.sshExecuteCmdWithResult(Mockito.any(Connection.class), Mockito.contains("virsh"))) + .thenReturn(new SSHCmdHelper.SSHCmdResult(0, "", "")); + sshCmdHelperMock.when(() -> SSHCmdHelper.sshExecuteCmd(Mockito.any(Connection.class), Mockito.anyString())) + .thenReturn(true); Mockito.doNothing().when(caManager).provisionCertificateViaSsh(Mockito.any(Connection.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); @@ -290,8 +294,9 @@ public void testProvisionKvmHostViaSsh() throws Exception { Assert.assertTrue(result); Mockito.verify(caManager, Mockito.times(1)).provisionCertificateViaSsh(Mockito.any(Connection.class), Mockito.eq("192.168.1.1"), Mockito.eq("host1"), Mockito.eq("root")); - sshCmdHelperMock.verify(() -> SSHCmdHelper.sshExecuteCmd(Mockito.any(Connection.class), Mockito.eq("systemctl restart libvirtd")), Mockito.times(1)); - sshCmdHelperMock.verify(() -> SSHCmdHelper.sshExecuteCmd(Mockito.any(Connection.class), Mockito.eq("systemctl restart cloudstack-agent")), Mockito.times(1)); + sshCmdHelperMock.verify(() -> SSHCmdHelper.sshExecuteCmdWithResult(Mockito.any(Connection.class), Mockito.contains("virsh")), Mockito.times(1)); + sshCmdHelperMock.verify(() -> SSHCmdHelper.sshExecuteCmd(Mockito.any(Connection.class), Mockito.contains("systemctl restart libvirtd")), Mockito.times(1)); + sshCmdHelperMock.verify(() -> SSHCmdHelper.sshExecuteCmd(Mockito.any(Connection.class), Mockito.contains("systemctl restart cloudstack-agent")), Mockito.times(1)); } }