mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
fix: address appliance v8 install review findings
- install: resolve the image directory from the version's settings type and skip image handling for docker appliances; guard appliance.images - appliance schema: validate template_properties against template_type, align cpu_throttling with the qemu template, add kvm and version idlepc - conversion: map IOU image to path, kvm disable to accel=tcg, inherit only same-type default settings, symbol fallback from the effective category, template_properties cannot override structural fields - allow clearing netmiko_device_type with an empty string - download the template symbol regardless of the level it is defined at and give qemu guests a default symbol
This commit is contained in:
10 files changed
+603
-39
No files matched your search
@@ -174,7 +174,7 @@ class ApplianceManager:
|
||||
version_images = version.get("images")
|
||||
if version_images:
|
||||
for appliance_key, appliance_file in version_images.items():
|
||||
for image in appliance.images:
|
||||
for image in appliance.images or []:
|
||||
if appliance_file == image.get("filename"):
|
||||
image_checksum = image.get("md5sum")
|
||||
image_in_db = await images_repo.get_image_by_checksum(image_checksum)
|
||||
@@ -225,12 +225,15 @@ class ApplianceManager:
|
||||
|
||||
from . import Controller
|
||||
|
||||
# downloading missing custom symbol for this appliance
|
||||
if appliance.symbol and not appliance.symbol.startswith(":/symbols/"):
|
||||
destination_path = os.path.join(Controller.instance().symbols.symbols_path(), appliance.symbol)
|
||||
template_data = ApplianceToTemplate().new_template(appliance.asdict(), version, "local") # FIXME: "local"
|
||||
# download the custom symbol used by the template if it is missing;
|
||||
# the symbol can be defined at the appliance, version or settings level
|
||||
symbol = template_data.get("symbol")
|
||||
if symbol and not symbol.startswith(":/symbols/"):
|
||||
destination_path = os.path.join(Controller.instance().symbols.symbols_path(), symbol)
|
||||
if not os.path.exists(destination_path):
|
||||
await self._download_symbol(appliance.symbol, destination_path)
|
||||
return ApplianceToTemplate().new_template(appliance.asdict(), version, "local") # FIXME: "local"
|
||||
await self._download_symbol(symbol, destination_path)
|
||||
return template_data
|
||||
|
||||
async def install_appliances_from_image(
|
||||
self,
|
||||
@@ -290,11 +293,14 @@ class ApplianceManager:
|
||||
if not appliance.versions:
|
||||
raise ControllerBadRequestError(message=f"Appliance '{appliance_id}' do not have versions")
|
||||
|
||||
image_dir = default_images_directory(appliance.type)
|
||||
for appliance_version_info in appliance.versions:
|
||||
if appliance_version_info.get("name") == version:
|
||||
try:
|
||||
await self._find_appliance_version_images(appliance, appliance_version_info, images_repo, image_dir)
|
||||
template_type = ApplianceToTemplate().get_template_type(appliance.asdict(), appliance_version_info)
|
||||
if template_type != "docker":
|
||||
# docker appliances have no image files to find or download
|
||||
image_dir = default_images_directory(template_type)
|
||||
await self._find_appliance_version_images(appliance, appliance_version_info, images_repo, image_dir)
|
||||
except InvalidImageError as e:
|
||||
raise ControllerError(message=f"Image error: {e}")
|
||||
template_data = await self._appliance_to_template(appliance, appliance_version_info)
|
||||
@@ -363,12 +369,14 @@ class ApplianceManager:
|
||||
category = appliance["category"]
|
||||
if category == "guest":
|
||||
if appliance.get("registry_version", 0) >= 8:
|
||||
# registry version 8: look for a Docker settings set instead of a top-level block
|
||||
settings_types = [
|
||||
settings.get("template_type") for settings in appliance.get("settings") or []
|
||||
]
|
||||
if "docker" in settings_types:
|
||||
# registry version 8: the emulator type comes from the default
|
||||
# settings set (or the only one present), not a top-level block
|
||||
settings = appliance.get("settings") or []
|
||||
selected = next((s for s in settings if s.get("default")), settings[0] if settings else None)
|
||||
if selected and selected.get("template_type") == "docker":
|
||||
return controller.symbols.get_default_symbol("docker_guest", symbol_theme)
|
||||
if selected:
|
||||
return controller.symbols.get_default_symbol("qemu_guest", symbol_theme)
|
||||
elif "docker" in appliance:
|
||||
return controller.symbols.get_default_symbol("docker_guest", symbol_theme)
|
||||
elif "qemu" in appliance:
|
||||
|
||||
@@ -168,9 +168,23 @@ class ApplianceToTemplate:
|
||||
elif appliance_config.get(prop) is not None:
|
||||
properties[prop] = appliance_config[prop]
|
||||
|
||||
if properties.get("category") == "multilayer_switch":
|
||||
category_before_remap = properties.get("category")
|
||||
if category_before_remap == "multilayer_switch":
|
||||
properties["category"] = "switch"
|
||||
|
||||
if settings["template_type"] == "qemu":
|
||||
# kvm is not a valid template property: convert it to the
|
||||
# equivalent qemu options like for registry versions 1-6
|
||||
kvm = properties.pop("kvm", None) or "allow"
|
||||
options = properties.get("options") or ""
|
||||
if kvm == "disable" and "-machine accel=tcg" not in options:
|
||||
options += " -machine accel=tcg"
|
||||
properties["options"] = options.strip()
|
||||
|
||||
# template_properties must not override the structural fields
|
||||
for reserved in ("template_type", "compute_id", "version"):
|
||||
properties.pop(reserved, None)
|
||||
|
||||
new_template.update(properties)
|
||||
if "tags" in appliance_config:
|
||||
new_template["tags"] = appliance_config.get("tags")
|
||||
@@ -179,8 +193,8 @@ class ApplianceToTemplate:
|
||||
new_template["netmiko_device_type"] = appliance_config["netmiko_device_type"]
|
||||
|
||||
if not new_template.get("symbol"):
|
||||
# apply a default symbol based on the category and template type
|
||||
if appliance_config["category"] == "guest":
|
||||
# apply a default symbol based on the effective category and template type
|
||||
if category_before_remap == "guest":
|
||||
if settings["template_type"] == "docker":
|
||||
new_template["symbol"] = ":/symbols/docker_guest.svg"
|
||||
else:
|
||||
@@ -192,13 +206,38 @@ class ApplianceToTemplate:
|
||||
"multilayer_switch": ":/symbols/multilayer_switch.svg",
|
||||
"firewall": ":/symbols/firewall.svg",
|
||||
}
|
||||
new_template["symbol"] = symbols.get(appliance_config["category"])
|
||||
new_template["symbol"] = symbols.get(category_before_remap)
|
||||
|
||||
if version and version.get("images"):
|
||||
new_template.update(version["images"])
|
||||
if settings["template_type"] == "iou":
|
||||
# IOU templates take the image path, not an image name
|
||||
new_template["path"] = version["images"].get("image")
|
||||
else:
|
||||
new_template.update(version["images"])
|
||||
|
||||
if version and settings["template_type"] == "dynamips" and version.get("idlepc"):
|
||||
# settings level idlepc takes precedence over the version level
|
||||
new_template.setdefault("idlepc", version["idlepc"])
|
||||
|
||||
return new_template
|
||||
|
||||
def get_template_type(self, appliance_config, version):
|
||||
"""
|
||||
Returns the template type of the settings set used to install the given
|
||||
version: for registry versions 1-6 it comes from the emulator block, for
|
||||
version 8 from the settings set selected for the version.
|
||||
"""
|
||||
|
||||
if appliance_config.get("registry_version", 0) >= 8:
|
||||
return self._select_v8_settings(appliance_config, version)["template_type"]
|
||||
if "iou" in appliance_config:
|
||||
return "iou"
|
||||
if "dynamips" in appliance_config:
|
||||
return "dynamips"
|
||||
if "docker" in appliance_config:
|
||||
return "docker"
|
||||
return "qemu"
|
||||
|
||||
def _select_v8_settings(self, appliance_config, version):
|
||||
"""
|
||||
Selects the settings set to use: the one referenced by the version,
|
||||
@@ -235,13 +274,15 @@ class ApplianceToTemplate:
|
||||
"""
|
||||
Merges the template properties of the selected settings with the default
|
||||
settings properties, unless inheritance is disabled or the default set
|
||||
is selected.
|
||||
is selected. Only a default set of the same emulator type is inherited
|
||||
from, so properties of a different type never pollute the template.
|
||||
"""
|
||||
|
||||
properties = {}
|
||||
if not settings.get("default") and settings.get("inherit_default_properties", True):
|
||||
for other_settings in appliance_config.get("settings") or []:
|
||||
if other_settings.get("default"):
|
||||
if other_settings.get("default") and other_settings.get("template_type") == settings["template_type"]:
|
||||
properties.update(other_settings.get("template_properties") or {})
|
||||
break
|
||||
properties.update(settings.get("template_properties") or {})
|
||||
return properties
|
||||
Reference in new issue
Block a user