From d0bab9947eb1753e8f907d0df2b4c94310bed42f Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 11 Nov 2018 21:07:33 +0800 Subject: [PATCH 1/2] Support tigervnc in Docker VM. Ref #1438 --- gns3server/compute/docker/docker_vm.py | 68 +++++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index ad24a987..b6c9b4da 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -24,6 +24,7 @@ import shutil import psutil import shlex import aiohttp +import subprocess import os from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer @@ -82,7 +83,8 @@ class DockerVM(BaseNode): self._ethernet_adapters = [] self._temporary_directory = None self._telnet_servers = [] - self._x11vnc_process = None + self._vnc_process = None + self._xvfb_process = None self._console_resolution = console_resolution self._console_http_path = console_http_path self._console_http_port = console_http_port @@ -517,19 +519,46 @@ class DockerVM(BaseNode): """ self._display = self._get_free_display_port() - if shutil.which("Xvfb") is None or shutil.which("x11vnc") is None: - raise DockerError("Please install Xvfb and x11vnc before using VNC support") - self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16") - # We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569 - self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host) + if shutil.which("Xtigervnc") is None or shutil.which("Xvfb") is None or shutil.which("x11vnc") is None: + raise DockerError("Please install tigervnc-standalone-server (recommended) or Xvfb + x11vnc before using VNC support") + + if shutil.which("Xtigervnc"): + with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd: + self._vnc_process = yield from asyncio.create_subprocess_exec("Xtigervnc", + "-geometry", self._console_resolution, + "-depth", "16", + "-interface", self._manager.port_manager.console_host, + "-rfbport", str(self.console), + "-AlwaysShared", + "-SecurityTypes", "None", + ":{}".format(self._display), + stdout=fd, stderr=subprocess.STDOUT) + else: + self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", + "-nolisten", + "tcp", ":{}".format(self._display), + "-screen", "0", + self._console_resolution + "x16") + + # We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569 + with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd: + self._vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", + "-forever", + "-nopw" + "-shared", + "-geometry", self._console_resolution, + "-display", "WAIT:{}".format(self._display), + "-rfbport", str(self.console), + "-rfbportv6", str(self.console), + "-noncache", + "-listen", self._manager.port_manager.console_host, + stdout=fd, stderr=subprocess.STDOUT) x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display)) yield from wait_for_file_creation(x11_socket) + #monitor_process(self._vnc_process, self._vnc_callback) - #monitor_process(self._xvfb_process, self._xvfb_callback) - #monitor_process(self._x11vnc_process, self._x11vnc_callback) - - def _xvfb_callback(self, returncode): + def _vnc_callback(self, returncode): """ Called when the process has stopped. @@ -537,17 +566,7 @@ class DockerVM(BaseNode): """ if returncode != 0: - self.project.emit("log.error", {"message": "The Xvfb process has stopped, return code: {}.".format(returncode)}) - - def _x11vnc_callback(self, returncode): - """ - Called when the process has stopped. - - :param returncode: Process returncode - """ - - if returncode != 0: - self.project.emit("log.error", {"message": "The x11vnc process has stopped, return code: {}.".format(returncode)}) + self.project.emit("log.error", {"message": "The vnc process has stopped, return code: {}.".format(returncode)}) @asyncio.coroutine def _start_http(self): @@ -725,12 +744,13 @@ class DockerVM(BaseNode): if state == "paused" or state == "running": yield from self.stop() if self.console_type == "vnc": - if self._x11vnc_process: + if self._vnc_process: try: - self._x11vnc_process.terminate() - yield from self._x11vnc_process.wait() + self._vnc_process.terminate() + yield from self._vnc_process.wait() except ProcessLookupError: pass + if self._xvfb_process: try: self._xvfb_process.terminate() yield from self._xvfb_process.wait() From d45d52f15c643c23cf44d0d9c402d42ce7672c90 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 11 Nov 2018 23:23:30 +0800 Subject: [PATCH 2/2] Support Xtigervnc restart. --- gns3server/compute/docker/docker_vm.py | 37 +++++++++++++++----------- tests/compute/docker/test_docker_vm.py | 5 ++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 07f30e4b..fe975fb2 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -83,7 +83,7 @@ class DockerVM(BaseNode): self._temporary_directory = None self._telnet_servers = [] self._xvfb_process = None - self._x11vnc_process = None + self._vnc_process = None self._console_resolution = console_resolution self._console_http_path = console_http_path self._console_http_port = console_http_port @@ -424,9 +424,10 @@ class DockerVM(BaseNode): return else: - if self._console_type == "vnc" and not self._x11vnc_process: - # start the x11vnc process in case it had previously crashed - self._x11vnc_process = await asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host) + if self._console_type == "vnc" and not self._vnc_process: + # restart the vnc process in case it had previously crashed + await self._start_vnc_process(restart=True) + monitor_process(self._vnc_process, self._vnc_callback) await self._clean_servers() @@ -521,15 +522,11 @@ class DockerVM(BaseNode): raise DockerError("Could not fix permissions for {}: {}".format(volume, e)) await process.wait() - async def _start_vnc(self): + async def _start_vnc_process(self, restart=False): """ - Starts a VNC server for this container + Starts the VNC process. """ - self._display = self._get_free_display_port() - if not (shutil.which("Xtigervnc") or shutil.which("Xvfb") and shutil.which("x11vnc")): - raise DockerError("Please install tigervnc-standalone-server (recommended) or Xvfb + x11vnc before using VNC support") - if shutil.which("Xtigervnc"): with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd: self._vnc_process = await asyncio.create_subprocess_exec("Xtigervnc", @@ -542,11 +539,12 @@ class DockerVM(BaseNode): ":{}".format(self._display), stdout=fd, stderr=subprocess.STDOUT) else: - self._xvfb_process = await asyncio.create_subprocess_exec("Xvfb", - "-nolisten", - "tcp", ":{}".format(self._display), - "-screen", "0", - self._console_resolution + "x16") + if restart is False: + self._xvfb_process = await asyncio.create_subprocess_exec("Xvfb", + "-nolisten", + "tcp", ":{}".format(self._display), + "-screen", "0", + self._console_resolution + "x16") # We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569 with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd: @@ -562,6 +560,15 @@ class DockerVM(BaseNode): "-listen", self._manager.port_manager.console_host, stdout=fd, stderr=subprocess.STDOUT) + async def _start_vnc(self): + """ + Starts a VNC server for this container + """ + + self._display = self._get_free_display_port() + if not (shutil.which("Xtigervnc") or shutil.which("Xvfb") and shutil.which("x11vnc")): + raise DockerError("Please install tigervnc-standalone-server (recommended) or Xvfb + x11vnc before using VNC support") + await self._start_vnc_process() x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display)) await wait_for_file_creation(x11_socket) diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index d1a5d8c7..ae34281a 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -975,12 +975,11 @@ def test_start_vnc(vm, loop): with asyncio_patch("asyncio.create_subprocess_exec") as mock_exec: loop.run_until_complete(asyncio.ensure_future(vm._start_vnc())) assert vm._display is not None - mock_exec.assert_any_call("Xvfb", "-nolisten", "tcp", ":{}".format(vm._display), "-screen", "0", "1280x1024x16") - mock_exec.assert_any_call("x11vnc", "-forever", "-nopw", "-shared", "-geometry", "1280x1024", "-display", "WAIT:{}".format(vm._display), "-rfbport", str(vm.console), "-rfbportv6", str(vm.console), "-noncache", "-listen", "127.0.0.1") + assert mock_exec.call_args[0] == ("Xtigervnc", "-geometry", vm.console_resolution, "-depth", "16", "-interface", "127.0.0.1", "-rfbport", str(vm.console), "-AlwaysShared", "-SecurityTypes", "None", ":{}".format(vm._display)) mock_wait.assert_called_with("/tmp/.X11-unix/X{}".format(vm._display)) -def test_start_vnc_xvfb_missing(vm, loop): +def test_start_vnc_missing(vm, loop): with pytest.raises(DockerError): loop.run_until_complete(asyncio.ensure_future(vm._start_vnc()))