mirror of
https://github.com/GNS3/gns3-server
synced 2024-12-26 00:38:10 +00:00
Optimize appliance templates update from GitHub repository by only downloading when the repository
has been updated. Ref https://github.com/GNS3/gns3-gui/issues/2490
This commit is contained in:
parent
651d8280a7
commit
b7f9b865c8
@ -35,6 +35,7 @@ from ..version import __version__
|
|||||||
from .topology import load_topology
|
from .topology import load_topology
|
||||||
from .gns3vm import GNS3VM
|
from .gns3vm import GNS3VM
|
||||||
from ..utils.get_resource import get_resource
|
from ..utils.get_resource import get_resource
|
||||||
|
from ..utils.asyncio import locked_coroutine
|
||||||
from .gns3vm.gns3_vm_error import GNS3VMError
|
from .gns3vm.gns3_vm_error import GNS3VMError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -49,7 +50,6 @@ class Controller:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._computes = {}
|
self._computes = {}
|
||||||
self._projects = {}
|
self._projects = {}
|
||||||
self._downloaded_appliance_templates_cache = {}
|
|
||||||
|
|
||||||
self._notification = Notification(self)
|
self._notification = Notification(self)
|
||||||
self.gns3vm = GNS3VM(self)
|
self.gns3vm = GNS3VM(self)
|
||||||
@ -59,36 +59,41 @@ class Controller:
|
|||||||
self._settings = None
|
self._settings = None
|
||||||
self._appliances = {}
|
self._appliances = {}
|
||||||
self._appliance_templates = {}
|
self._appliance_templates = {}
|
||||||
|
self._appliance_templates_etag = None
|
||||||
|
|
||||||
self._config_file = os.path.join(Config.instance().config_dir, "gns3_controller.conf")
|
self._config_file = os.path.join(Config.instance().config_dir, "gns3_controller.conf")
|
||||||
log.info("Load controller configuration file {}".format(self._config_file))
|
log.info("Load controller configuration file {}".format(self._config_file))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@locked_coroutine
|
||||||
def download_appliance_templates(self):
|
def download_appliance_templates(self):
|
||||||
|
|
||||||
session = aiohttp.ClientSession()
|
session = aiohttp.ClientSession()
|
||||||
response = yield from session.get('https://api.github.com/repos/GNS3/gns3-registry/contents/appliances')
|
|
||||||
json_data = yield from response.json()
|
|
||||||
if response.status != 200:
|
|
||||||
raise aiohttp.web.HTTPConflict(text="Could not retrieve appliance templates on GitHub")
|
|
||||||
response.close()
|
|
||||||
try:
|
try:
|
||||||
|
headers = {}
|
||||||
|
if self._appliance_templates_etag:
|
||||||
|
log.info("Checking if appliance templates are up-to-date (ETag {})".format(self._appliance_templates_etag))
|
||||||
|
headers["If-None-Match"] = self._appliance_templates_etag
|
||||||
|
response = yield from session.get('https://api.github.com/repos/GNS3/gns3-registry/contents/appliances', headers=headers)
|
||||||
|
if response.status == 304:
|
||||||
|
log.info("Appliance templates are already up-to-date (ETag {})".format(self._appliance_templates_etag))
|
||||||
|
return
|
||||||
|
elif response.status != 200:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Could not retrieve appliance templates on GitHub")
|
||||||
|
etag = response.headers.get("ETag")
|
||||||
|
if etag:
|
||||||
|
self._appliance_templates_etag = etag
|
||||||
|
self.save()
|
||||||
|
json_data = yield from response.json()
|
||||||
|
response.close()
|
||||||
appliances_dir = get_resource('appliances')
|
appliances_dir = get_resource('appliances')
|
||||||
for appliance in json_data:
|
for appliance in json_data:
|
||||||
if appliance["type"] == "file":
|
if appliance["type"] == "file":
|
||||||
headers = {}
|
|
||||||
appliance_name = appliance["name"]
|
appliance_name = appliance["name"]
|
||||||
#if appliance_name in self._downloaded_appliance_templates_cache:
|
log.info("Download appliance template file from '{}'".format(appliance["download_url"]))
|
||||||
# headers["If-None-Match"] = self._downloaded_appliance_templates_cache[appliance_name]
|
response = yield from session.get(appliance["download_url"])
|
||||||
#log.debug("Download appliance template file from '{}'".format(appliance["download_url"]))
|
|
||||||
response = yield from session.get(appliance["download_url"], headers=headers)
|
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
log.debug("Could not download '{}'".format(appliance["download_url"]))
|
log.warning("Could not download '{}'".format(appliance["download_url"]))
|
||||||
continue
|
continue
|
||||||
#if resp.status == 304:
|
|
||||||
# log.debug("{} is already up-to-date".format(appliance_name))
|
|
||||||
# continue
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
appliance_data = yield from response.read()
|
appliance_data = yield from response.read()
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
@ -97,15 +102,11 @@ class Controller:
|
|||||||
path = os.path.join(appliances_dir, appliance_name)
|
path = os.path.join(appliances_dir, appliance_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.debug("Saving {} file to {}".format(appliance_name, path))
|
log.info("Saving {} file to {}".format(appliance_name, path))
|
||||||
with open(path, 'wb') as f:
|
with open(path, 'wb') as f:
|
||||||
f.write(appliance_data)
|
f.write(appliance_data)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPConflict(text="Could not write appliance template file '{}': {}".format(path, e))
|
raise aiohttp.web.HTTPConflict(text="Could not write appliance template file '{}': {}".format(path, e))
|
||||||
|
|
||||||
#etag = response.headers.get("ETag")
|
|
||||||
#if etag:
|
|
||||||
# self._downloaded_appliance_templates_cache[appliance_name] = etag
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise aiohttp.web.HTTPConflict(text="Could not read appliance templates information from GitHub: {}".format(e))
|
raise aiohttp.web.HTTPConflict(text="Could not read appliance templates information from GitHub: {}".format(e))
|
||||||
finally:
|
finally:
|
||||||
@ -295,6 +296,7 @@ class Controller:
|
|||||||
"computes": [],
|
"computes": [],
|
||||||
"settings": self._settings,
|
"settings": self._settings,
|
||||||
"gns3vm": self.gns3vm.__json__(),
|
"gns3vm": self.gns3vm.__json__(),
|
||||||
|
"appliance_templates_etag": self._appliance_templates_etag,
|
||||||
"version": __version__
|
"version": __version__
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,6 +342,7 @@ class Controller:
|
|||||||
if "gns3vm" in data:
|
if "gns3vm" in data:
|
||||||
self.gns3vm.settings = data["gns3vm"]
|
self.gns3vm.settings = data["gns3vm"]
|
||||||
|
|
||||||
|
self._appliance_templates_etag = data.get("appliance_templates_etag")
|
||||||
self.load_appliance_templates()
|
self.load_appliance_templates()
|
||||||
self.load_appliances()
|
self.load_appliances()
|
||||||
return data.get("computes", [])
|
return data.get("computes", [])
|
||||||
|
@ -39,7 +39,7 @@ class ApplianceHandler:
|
|||||||
controller = Controller.instance()
|
controller = Controller.instance()
|
||||||
if request.query.get("update", "no") == "yes":
|
if request.query.get("update", "no") == "yes":
|
||||||
yield from controller.download_appliance_templates()
|
yield from controller.download_appliance_templates()
|
||||||
controller.load_appliance_templates()
|
controller.load_appliance_templates()
|
||||||
response.json([c for c in controller.appliance_templates.values()])
|
response.json([c for c in controller.appliance_templates.values()])
|
||||||
|
|
||||||
@Route.get(
|
@Route.get(
|
||||||
|
@ -163,8 +163,8 @@ def wait_for_named_pipe_creation(pipe_path, timeout=60):
|
|||||||
|
|
||||||
def locked_coroutine(f):
|
def locked_coroutine(f):
|
||||||
"""
|
"""
|
||||||
Method decorator that replace asyncio.coroutine that warranty
|
Method decorator that replace asyncio.coroutine that guarantee
|
||||||
that this specific method of this class instance will not we
|
that this specific method of this class instance will not be
|
||||||
executed twice at the same time
|
executed twice at the same time
|
||||||
"""
|
"""
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -22,7 +22,7 @@ import os
|
|||||||
|
|
||||||
class FileWatcher:
|
class FileWatcher:
|
||||||
"""
|
"""
|
||||||
Watch for file change and call the callback when something happen
|
Watch for file change and call the callback when something happens
|
||||||
|
|
||||||
:param paths: A path or a list of file to watch
|
:param paths: A path or a list of file to watch
|
||||||
:param delay: Delay between file check (seconds)
|
:param delay: Delay between file check (seconds)
|
||||||
|
Loading…
Reference in New Issue
Block a user