From 77d4eabadcbceb904e26bace34cb229b950cf910 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 17:31:58 +1000 Subject: [PATCH 1/9] Catch ConnectionResetError exception when client disconnects --- gns3server/web/route.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gns3server/web/route.py b/gns3server/web/route.py index 115d209c..4a7103a3 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -232,8 +232,7 @@ class Route(object): response.set_status(408) response.json({"message": "Request canceled", "status": 408}) raise # must raise to let aiohttp know the connection has been closed - except aiohttp.ClientError: - log.warning("Client error") + except (ConnectionResetError, aiohttp.ClientError): response = Response(request=request, route=route) response.set_status(408) response.json({"message": "Client error", "status": 408}) From 2d7438446cfaf8bd9b7af557eb9fb15e05002bc7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 17:47:48 +1000 Subject: [PATCH 2/9] Upgrade dependencies --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index ba457a97..7116e212 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -jsonschema>=4.17.3,<4.18; python_version >= '3.7' +jsonschema>=4.19.0,<4.20; python_version >= '3.7' jsonschema==3.2.0; python_version < '3.7' # v3.2.0 is the last version to support Python 3.6 -aiohttp>=3.8.4,<3.9 +aiohttp>=3.8.5,<3.9 aiohttp-cors>=0.7.0,<0.8 -aiofiles>=23.1.0,<23.2; python_version >= '3.7' +aiofiles>=23.2.1,<23.3; python_version >= '3.7' aiofiles==0.8.0; python_version < '3.7' # v0.8.0 is the last version to support Python 3.6 Jinja2>=3.1.2,<3.2; python_version >= '3.7' Jinja2==3.0.3; python_version < '3.7' # v3.0.3 is the last version to support Python 3.6 From 6a614fbd78b630c2473cd702f4211180e8c24b1a Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 17:51:24 +1000 Subject: [PATCH 3/9] Downgrade jsonschema --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7116e212..de280d34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -jsonschema>=4.19.0,<4.20; python_version >= '3.7' +jsonschema>=4.17.3,<4.18; python_version >= '3.7' # v4.17.3 is the last version to support Python 3.7 jsonschema==3.2.0; python_version < '3.7' # v3.2.0 is the last version to support Python 3.6 aiohttp>=3.8.5,<3.9 aiohttp-cors>=0.7.0,<0.8 From df2f96828eeefc2ed60fce9697843ebc751d11d6 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 18:48:43 +1000 Subject: [PATCH 4/9] Use the user data dir to store built-in appliances --- gns3server/controller/__init__.py | 2 ++ gns3server/controller/appliance_manager.py | 13 +++++++------ requirements.txt | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 2220f7e5..19d7976e 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -255,6 +255,8 @@ class Controller: if not previous_version or \ parse_version(__version__.split("+")[0]) > parse_version(previous_version.split("+")[0]): self._appliance_manager.install_builtin_appliances() + elif not os.listdir(self._appliance_manager.builtin_appliances_path()): + self._appliance_manager.install_builtin_appliances() self._appliance_manager.appliances_etag = controller_settings.get("appliances_etag") self._appliance_manager.load_appliances() diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py index 76cadfbf..d6878d71 100644 --- a/gns3server/controller/appliance_manager.py +++ b/gns3server/controller/appliance_manager.py @@ -21,6 +21,7 @@ import uuid import asyncio import aiohttp import shutil +import platformdirs try: @@ -81,13 +82,13 @@ class ApplianceManager: os.makedirs(appliances_path, exist_ok=True) return appliances_path - def _builtin_appliances_path(self, delete_first=False): + def builtin_appliances_path(self, delete_first=False): """ Get the built-in appliance storage directory """ - config = Config.instance() - appliances_dir = os.path.join(config.config_dir, "appliances") + appname = vendor = "GNS3" + appliances_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "appliances") if delete_first: shutil.rmtree(appliances_dir, ignore_errors=True) os.makedirs(appliances_dir, exist_ok=True) @@ -98,7 +99,7 @@ class ApplianceManager: At startup we copy the built-in appliances files. """ - dst_path = self._builtin_appliances_path(delete_first=True) + dst_path = self.builtin_appliances_path(delete_first=True) log.info(f"Installing built-in appliances in '{dst_path}'") from . import Controller try: @@ -112,7 +113,7 @@ class ApplianceManager: """ self._appliances = {} - for directory, builtin in ((self._builtin_appliances_path(), True,), (self._custom_appliances_path(), False,)): + for directory, builtin in ((self.builtin_appliances_path(), True,), (self._custom_appliances_path(), False,)): if directory and os.path.isdir(directory): for file in os.listdir(directory): if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'): @@ -215,7 +216,7 @@ class ApplianceManager: from . import Controller Controller.instance().save() json_data = await response.json() - appliances_dir = self._builtin_appliances_path() + appliances_dir = self.builtin_appliances_path() downloaded_appliance_files = [] for appliance in json_data: if appliance["type"] == "file": diff --git a/requirements.txt b/requirements.txt index de280d34..392d35df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ psutil==5.9.5 async-timeout>=4.0.2,<4.1 distro>=1.8.0 py-cpuinfo>=9.0.0,<10.0 +platformdirs>=3.10.0 importlib-resources>=1.3; python_version <= '3.9' truststore>=0.7.0; python_version >= '3.10' setuptools>=60.8.1; python_version >= '3.7' From 090d1c8c84c4b6859d60e38fe2d46388f4c5fd9e Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 19:04:14 +1000 Subject: [PATCH 5/9] Only use platformdirs with Python >= '3.7' --- gns3server/controller/appliance_manager.py | 11 ++++++++--- requirements.txt | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py index d6878d71..53daed1b 100644 --- a/gns3server/controller/appliance_manager.py +++ b/gns3server/controller/appliance_manager.py @@ -21,7 +21,6 @@ import uuid import asyncio import aiohttp import shutil -import platformdirs try: @@ -87,8 +86,14 @@ class ApplianceManager: Get the built-in appliance storage directory """ - appname = vendor = "GNS3" - appliances_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "appliances") + try: + import platformdirs + appname = vendor = "GNS3" + appliances_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "appliances") + except ImportError: + # platformdirs is not available on Python 3.6, use the old method + config = Config.instance() + appliances_dir = os.path.join(config.config_dir, "appliances") if delete_first: shutil.rmtree(appliances_dir, ignore_errors=True) os.makedirs(appliances_dir, exist_ok=True) diff --git a/requirements.txt b/requirements.txt index 392d35df..67b604f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ psutil==5.9.5 async-timeout>=4.0.2,<4.1 distro>=1.8.0 py-cpuinfo>=9.0.0,<10.0 -platformdirs>=3.10.0 +platformdirs>=3.10.0; python_version >= '3.7' importlib-resources>=1.3; python_version <= '3.9' truststore>=0.7.0; python_version >= '3.10' setuptools>=60.8.1; python_version >= '3.7' From a69feb3682d983cbf263d33209cab604c357ec8b Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 12 Aug 2023 19:15:29 +1000 Subject: [PATCH 6/9] Use an older version of platformdirs --- gns3server/controller/appliance_manager.py | 11 +++-------- requirements.txt | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py index 53daed1b..d6878d71 100644 --- a/gns3server/controller/appliance_manager.py +++ b/gns3server/controller/appliance_manager.py @@ -21,6 +21,7 @@ import uuid import asyncio import aiohttp import shutil +import platformdirs try: @@ -86,14 +87,8 @@ class ApplianceManager: Get the built-in appliance storage directory """ - try: - import platformdirs - appname = vendor = "GNS3" - appliances_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "appliances") - except ImportError: - # platformdirs is not available on Python 3.6, use the old method - config = Config.instance() - appliances_dir = os.path.join(config.config_dir, "appliances") + appname = vendor = "GNS3" + appliances_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "appliances") if delete_first: shutil.rmtree(appliances_dir, ignore_errors=True) os.makedirs(appliances_dir, exist_ok=True) diff --git a/requirements.txt b/requirements.txt index 67b604f2..3153ce45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ psutil==5.9.5 async-timeout>=4.0.2,<4.1 distro>=1.8.0 py-cpuinfo>=9.0.0,<10.0 -platformdirs>=3.10.0; python_version >= '3.7' +platformdirs>=2.4.0 importlib-resources>=1.3; python_version <= '3.9' truststore>=0.7.0; python_version >= '3.10' setuptools>=60.8.1; python_version >= '3.7' From d6e1ee5dbb0c21badc48666042104e6593b7233f Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 27 Aug 2023 18:30:37 +1000 Subject: [PATCH 7/9] Prevent X11 socket file to be modified by Docker container --- gns3server/compute/docker/docker_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index a10312e3..500e526d 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -406,7 +406,7 @@ class DockerVM(BaseNode): await self._start_vnc() params["Env"].append("QT_GRAPHICSSYSTEM=native") # To fix a Qt issue: https://github.com/GNS3/gns3-server/issues/556 params["Env"].append("DISPLAY=:{}".format(self._display)) - params["HostConfig"]["Binds"].append("/tmp/.X11-unix/:/tmp/.X11-unix/") + params["HostConfig"]["Binds"].append("/tmp/.X11-unix/X{0}:/tmp/.X11-unix/X{0}:ro".format(self._display)) if self._extra_hosts: extra_hosts = self._format_extra_hosts(self._extra_hosts) From c2783d355f638d0ae2a1678f0d9a1242a208e50a Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 27 Aug 2023 18:41:25 +1000 Subject: [PATCH 8/9] Fix test_create_vnc test --- tests/compute/docker/test_docker_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index 9396cc89..3a395a56 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -182,7 +182,7 @@ async def test_create_vnc(compute_project, manager): "Binds": [ "{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), - '/tmp/.X11-unix/:/tmp/.X11-unix/' + "/tmp/.X11-unix/X{0}:/tmp/.X11-unix/X{0}:ro".format(vm._display) ], "Privileged": True }, From 709aa460747d74cbfb3b1c640017d4be6dc586f0 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 6 Sep 2023 16:48:24 +0700 Subject: [PATCH 9/9] Fix issue with controller config saved before checking current version with previous one --- gns3server/controller/__init__.py | 44 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 19d7976e..101854c9 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -191,29 +191,28 @@ class Controller: Save the controller configuration on disk """ - if self._config_loaded is False: - return + controller_settings = dict() + if self._config_loaded: + controller_settings = {"computes": [], + "templates": [], + "gns3vm": self.gns3vm.__json__(), + "iou_license": self._iou_license_settings, + "appliances_etag": self._appliance_manager.appliances_etag, + "version": __version__} - controller_settings = {"computes": [], - "templates": [], - "gns3vm": self.gns3vm.__json__(), - "iou_license": self._iou_license_settings, - "appliances_etag": self._appliance_manager.appliances_etag, - "version": __version__} + for template in self._template_manager.templates.values(): + if not template.builtin: + controller_settings["templates"].append(template.__json__()) - for template in self._template_manager.templates.values(): - if not template.builtin: - controller_settings["templates"].append(template.__json__()) - - for compute in self._computes.values(): - if compute.id != "local" and compute.id != "vm": - controller_settings["computes"].append({"host": compute.host, - "name": compute.name, - "port": compute.port, - "protocol": compute.protocol, - "user": compute.user, - "password": compute.password, - "compute_id": compute.id}) + for compute in self._computes.values(): + if compute.id != "local" and compute.id != "vm": + controller_settings["computes"].append({"host": compute.host, + "name": compute.name, + "port": compute.port, + "protocol": compute.protocol, + "user": compute.user, + "password": compute.password, + "compute_id": compute.id}) try: os.makedirs(os.path.dirname(self._config_file), exist_ok=True) @@ -229,8 +228,7 @@ class Controller: try: if not os.path.exists(self._config_file): - self._config_loaded = True - self.save() + self.save() # this will create the config file with open(self._config_file) as f: controller_settings = json.load(f) except (OSError, ValueError) as e: