mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 14:01:15 +02:00
fix: dedupe and report automatic template creation from images
install_appliances_from_image relied on the name+version pair check in TemplatesService, so the same appliance reached through a second image (the CSR1000v case) created a second template sharing the name. The auto path now skips when any template with the same name exists, whatever the version, and returns a manifest of created and skipped candidates; POST /images/install replies 200 with that manifest instead of an empty 204, and the image_install MCP tool surfaces it.
This commit is contained in:
7 files changed
+154
-11
No files matched your search
@@ -245,11 +245,16 @@ class ApplianceManager:
|
||||
rbac_repo: RbacRepository,
|
||||
current_user: schemas.User,
|
||||
image_dir: str
|
||||
) -> None:
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Install appliances using an image checksum
|
||||
Install appliances using an image checksum.
|
||||
|
||||
Returns a manifest of what happened: one entry per attempted template,
|
||||
either {"status": "created", ...template fields} or
|
||||
{"status": "skipped", "name", "reason"}.
|
||||
"""
|
||||
|
||||
results: List[dict] = []
|
||||
appliances_info = self._find_appliances_from_image_checksum(image_checksum)
|
||||
for appliance, image_version in appliances_info:
|
||||
try:
|
||||
@@ -257,15 +262,48 @@ class ApplianceManager:
|
||||
ApplianceModel.model_validate(appliance.asdict())
|
||||
except ValidationError as e:
|
||||
log.warning(f"Could not validate appliance '{appliance.id}': {e}")
|
||||
results.append({
|
||||
"status": "skipped",
|
||||
"name": appliance.name,
|
||||
"reason": f"could not validate appliance '{appliance.id}': {e}",
|
||||
})
|
||||
continue
|
||||
if appliance.versions:
|
||||
for version in appliance.versions:
|
||||
if version.get("name") == image_version:
|
||||
try:
|
||||
await self._find_appliance_version_images(appliance, version, images_repo, image_dir)
|
||||
template_data = await self._appliance_to_template(appliance, version)
|
||||
await self._create_template(template_data, templates_repo, rbac_repo, current_user)
|
||||
name = template_data.get("name")
|
||||
existing = await templates_repo.get_template_by_name(name) if name else None
|
||||
if existing is not None:
|
||||
# never automatically create a second template with the same
|
||||
# name: the name+version check in TemplatesService would allow
|
||||
# duplicates when the appliance version differs, but two
|
||||
# templates sharing a name is never what the user asked for here
|
||||
log.warning(f"Template '{name}' already exists, skipping automatic template creation")
|
||||
results.append({
|
||||
"status": "skipped",
|
||||
"name": name,
|
||||
"reason": f"a template named '{name}' already exists",
|
||||
})
|
||||
continue
|
||||
template = await self._create_template(template_data, templates_repo, rbac_repo, current_user)
|
||||
results.append({
|
||||
"status": "created",
|
||||
"template_id": str(template.get("template_id")),
|
||||
"name": template.get("name"),
|
||||
"version": template.get("version"),
|
||||
"template_type": template.get("template_type"),
|
||||
})
|
||||
except (ControllerError, InvalidImageError) as e:
|
||||
log.warning(f"Could not automatically create template using image '{image_path}': {e}")
|
||||
results.append({
|
||||
"status": "skipped",
|
||||
"name": appliance.name,
|
||||
"reason": str(e),
|
||||
})
|
||||
return results
|
||||
|
||||
async def install_appliance(
|
||||
self,
|
||||
|
||||
Reference in new issue
Block a user