appliance: seed netmiko_device_type from the appliance file

Both the v1-6 and v8 appliance models accept an optional top-level
netmiko_device_type, and ApplianceToTemplate copies it into the created
template so installed appliances carry the automation hint end to end.
This commit is contained in:
YueGuobin 2026-08-16 12:50:01 +08:00
parent d64f47afa4
commit f524e9a713
No known key found for this signature in database
3 changed files with 43 additions and 0 deletions

View File

@ -58,6 +58,9 @@ class ApplianceToTemplate:
if "tags" in appliance_config:
new_template["tags"] = appliance_config.get("tags")
if appliance_config.get("netmiko_device_type"):
new_template["netmiko_device_type"] = appliance_config["netmiko_device_type"]
if new_template.get("symbol") is None:
if appliance_config["category"] == "guest":
if "docker" in appliance_config:
@ -172,6 +175,9 @@ class ApplianceToTemplate:
if "tags" in appliance_config:
new_template["tags"] = appliance_config.get("tags")
if appliance_config.get("netmiko_device_type"):
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":

View File

@ -632,6 +632,9 @@ class ApplianceV1_6(BaseModel):
maintainer_email: Optional[Union[EmailStr, Annotated[str, Field(max_length=0)]]] = Field(None, title='Maintainer email')
usage: Optional[str] = Field(None, title='How to use the appliance')
symbol: Optional[str] = Field(None, title='An optional symbol for the appliance')
netmiko_device_type: Optional[str] = Field(
None, title='Device type for Netmiko-based automation tools', pattern=r'^[a-z0-9_]+$'
)
first_port_name: Optional[str] = Field(None, title='Optional name of the first networking port example: eth0')
port_name_format: Optional[str] = Field(None, title='Optional formating of the networking port example: eth{0}')
port_segment_size: Optional[int] = Field(
@ -685,6 +688,9 @@ class ApplianceV8(BaseModel):
default_username: Optional[str] = Field(None, title='Default username for the appliance')
default_password: Optional[str] = Field(None, title='Default password for the appliance')
symbol: Optional[str] = Field(None, title='An optional symbol for the appliance')
netmiko_device_type: Optional[str] = Field(
None, title='Device type for Netmiko-based automation tools', pattern=r'^[a-z0-9_]+$'
)
tags: Optional[List[str]] = Field(None, title='User-defined metadata tags for the appliance')
settings: List[TemplateSetting] = Field(..., title='Settings for running the appliance')
images: Optional[List[ApplianceImage]] = Field(None, title='Images for this appliance')

View File

@ -16,9 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
import pydantic
from gns3server.controller.appliance_to_template import ApplianceToTemplate
from gns3server.controller.controller_error import ControllerError
from gns3server.schemas.controller.appliances import ApplianceModel
# reduced mirror of the upstream vyos.gns3a (registry version 8, qemu, 2 settings sets)
@ -93,6 +95,21 @@ VYOS_V8 = {
}
def test_v8_netmiko_device_type_copied_to_template():
appliance = dict(VYOS_V8, netmiko_device_type="vyos_ssh")
template = ApplianceToTemplate().new_template(appliance, VYOS_V8["versions"][1], "local")
assert template["netmiko_device_type"] == "vyos_ssh"
def test_v8_netmiko_device_type_validates():
model = ApplianceModel.model_validate(dict(VYOS_V8, netmiko_device_type="vyos_ssh"))
assert model.netmiko_device_type == "vyos_ssh"
with pytest.raises(pydantic.ValidationError):
ApplianceModel.model_validate(dict(VYOS_V8, netmiko_device_type="Not Valid!"))
def test_v8_version_referenced_settings_with_inheritance():
"""
A version referencing a named settings set must select it and inherit
@ -283,3 +300,17 @@ def test_v6_path_unchanged():
assert template["console_type"] == "docker_exec"
assert template["adapters"] == 35
assert template["usage"] == "v6 usage"
def test_v6_netmiko_device_type_copied_to_template():
appliance = {
"registry_version": 6,
"name": "SRLinux",
"category": "router",
"symbol": ":/symbols/router.svg",
"netmiko_device_type": "nokia_srl",
"docker": {"adapters": 35, "image": "ghcr.io/nokia/srlinux:latest"},
}
template = ApplianceToTemplate().new_template(appliance, None, "local")
assert template["netmiko_device_type"] == "nokia_srl"