Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overwrite user resources when the originals have changed #2443

Merged
merged 1 commit into from
Nov 10, 2024
Merged
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
19 changes: 14 additions & 5 deletions gns3server/controller/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from importlib import resources as importlib_resources

from ..config import Config
from ..utils import parse_version
from ..utils import parse_version, md5sum
from ..utils.images import default_images_directory

from .project import Project
Expand Down Expand Up @@ -289,12 +289,21 @@ async def load_projects(self):
except OSError as e:
log.error(str(e))


@staticmethod
def install_resource_files(dst_path, resource_name):
def install_resource_files(dst_path, resource_name, upgrade_resources=True):
"""
Install files from resources to user's file system
"""

def should_copy(src, dst, upgrade_resources):
if not os.path.exists(dst):
return True
if upgrade_resources is False:
return False
# copy the resource if it is different
return md5sum(src) != md5sum(dst)

if hasattr(sys, "frozen") and sys.platform.startswith("win"):
resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name))
for filename in os.listdir(resource_path):
Expand All @@ -303,7 +312,7 @@ def install_resource_files(dst_path, resource_name):
else:
for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir():
full_path = os.path.join(dst_path, entry.name)
if entry.is_file() and not os.path.exists(full_path):
if entry.is_file() and should_copy(str(entry), full_path, upgrade_resources):
log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
shutil.copy(str(entry), os.path.join(dst_path, entry.name))
elif entry.is_dir():
Expand All @@ -319,7 +328,7 @@ def _install_base_configs(self):
dst_path = self.configs_path()
log.info(f"Installing base configs in '{dst_path}'")
try:
Controller.install_resource_files(dst_path, "configs")
Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
except OSError as e:
log.error(f"Could not install base config files to {dst_path}: {e}")

Expand All @@ -332,7 +341,7 @@ def _install_builtin_disks(self):
dst_path = self.disks_path()
log.info(f"Installing built-in disks in '{dst_path}'")
try:
Controller.install_resource_files(dst_path, "disks")
Controller.install_resource_files(dst_path, "disks", upgrade_resources=False)
except OSError as e:
log.error(f"Could not install disk files to {dst_path}: {e}")

Expand Down
6 changes: 2 additions & 4 deletions gns3server/controller/appliance_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def _custom_appliances_path(self):
os.makedirs(appliances_path, exist_ok=True)
return appliances_path

def builtin_appliances_path(self, delete_first=False):
def builtin_appliances_path(self):
"""
Get the built-in appliance storage directory
"""
Expand All @@ -91,8 +91,6 @@ def builtin_appliances_path(self, delete_first=False):
appname = vendor = "GNS3"
resources_path = os.path.expanduser(server_config.get("resources_path", platformdirs.user_data_dir(appname, vendor, roaming=True)))
appliances_dir = os.path.join(resources_path, "appliances")
if delete_first:
shutil.rmtree(appliances_dir, ignore_errors=True)
os.makedirs(appliances_dir, exist_ok=True)
return appliances_dir

Expand All @@ -101,7 +99,7 @@ def install_builtin_appliances(self):
At startup we copy the built-in appliances files.
"""

dst_path = self.builtin_appliances_path(delete_first=True)
dst_path = self.builtin_appliances_path()
log.info(f"Installing built-in appliances in '{dst_path}'")
from . import Controller
try:
Expand Down
12 changes: 12 additions & 0 deletions gns3server/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import posixpath
import socket
import errno
import hashlib


def force_unix_path(path):
Expand Down Expand Up @@ -120,3 +121,14 @@ def is_ipv6_enabled() -> bool:
if e.errno == errno.EADDRINUSE:
return True
raise

def md5sum(filename):
"""
Calculate the MD5 checksum of a file.
"""

hash_md5 = hashlib.md5()
with open(filename, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ psutil>=6.1.0
async-timeout>=4.0.3,<4.1
distro>=1.9.0
py-cpuinfo>=9.0.0,<10.0
platformdirs>=2.4.0
platformdirs>=2.4.0,<3 # platformdirs >=3 conflicts when building Debian packages
importlib-resources>=1.3; python_version < '3.9'
truststore>=0.10.0; python_version >= '3.10'