Overwrite user resources when the originals have changed.

Excepting configs and disks which can be customized.
This commit is contained in:
grossmj 2024-11-09 12:17:06 +10:00
parent db1fb29c63
commit a60e0d424d
No known key found for this signature in database
GPG Key ID: 0A2D76AC45EA25CD
4 changed files with 29 additions and 10 deletions

View File

@ -29,7 +29,7 @@ except ImportError:
from importlib import resources as importlib_resources from importlib import resources as importlib_resources
from ..config import Config from ..config import Config
from ..utils import parse_version from ..utils import parse_version, md5sum
from ..utils.images import default_images_directory from ..utils.images import default_images_directory
from .project import Project from .project import Project
@ -289,12 +289,21 @@ class Controller:
except OSError as e: except OSError as e:
log.error(str(e)) log.error(str(e))
@staticmethod @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 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"): if hasattr(sys, "frozen") and sys.platform.startswith("win"):
resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name)) resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name))
for filename in os.listdir(resource_path): for filename in os.listdir(resource_path):
@ -303,7 +312,7 @@ class Controller:
else: else:
for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir(): for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir():
full_path = os.path.join(dst_path, entry.name) 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}"') log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
shutil.copy(str(entry), os.path.join(dst_path, entry.name)) shutil.copy(str(entry), os.path.join(dst_path, entry.name))
elif entry.is_dir(): elif entry.is_dir():
@ -319,7 +328,7 @@ class Controller:
dst_path = self.configs_path() dst_path = self.configs_path()
log.info(f"Installing base configs in '{dst_path}'") log.info(f"Installing base configs in '{dst_path}'")
try: try:
Controller.install_resource_files(dst_path, "configs") Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
except OSError as e: except OSError as e:
log.error(f"Could not install base config files to {dst_path}: {e}") log.error(f"Could not install base config files to {dst_path}: {e}")
@ -332,7 +341,7 @@ class Controller:
dst_path = self.disks_path() dst_path = self.disks_path()
log.info(f"Installing built-in disks in '{dst_path}'") log.info(f"Installing built-in disks in '{dst_path}'")
try: try:
Controller.install_resource_files(dst_path, "disks") Controller.install_resource_files(dst_path, "disks", upgrade_resources=False)
except OSError as e: except OSError as e:
log.error(f"Could not install disk files to {dst_path}: {e}") log.error(f"Could not install disk files to {dst_path}: {e}")

View File

@ -82,7 +82,7 @@ class ApplianceManager:
os.makedirs(appliances_path, exist_ok=True) os.makedirs(appliances_path, exist_ok=True)
return appliances_path return appliances_path
def builtin_appliances_path(self, delete_first=False): def builtin_appliances_path(self):
""" """
Get the built-in appliance storage directory Get the built-in appliance storage directory
""" """
@ -91,8 +91,6 @@ class ApplianceManager:
appname = vendor = "GNS3" appname = vendor = "GNS3"
resources_path = os.path.expanduser(server_config.get("resources_path", platformdirs.user_data_dir(appname, vendor, roaming=True))) 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") 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) os.makedirs(appliances_dir, exist_ok=True)
return appliances_dir return appliances_dir
@ -101,7 +99,7 @@ class ApplianceManager:
At startup we copy the built-in appliances files. 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}'") log.info(f"Installing built-in appliances in '{dst_path}'")
from . import Controller from . import Controller
try: try:

View File

@ -23,6 +23,7 @@ import textwrap
import posixpath import posixpath
import socket import socket
import errno import errno
import hashlib
def force_unix_path(path): def force_unix_path(path):
@ -120,3 +121,14 @@ def is_ipv6_enabled() -> bool:
if e.errno == errno.EADDRINUSE: if e.errno == errno.EADDRINUSE:
return True return True
raise 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()

View File

@ -8,6 +8,6 @@ psutil>=6.1.0
async-timeout>=4.0.3,<4.1 async-timeout>=4.0.3,<4.1
distro>=1.9.0 distro>=1.9.0
py-cpuinfo>=9.0.0,<10.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' importlib-resources>=1.3; python_version < '3.9'
truststore>=0.10.0; python_version >= '3.10' truststore>=0.10.0; python_version >= '3.10'