Merge branch '3.1' into images-nested-folders-support

This commit is contained in:
Jeremy Grossmann authored and GitHub committed 2026-10-06 16:50:40 +02:00
commit f184883aa7
108 files changed
+919 -1844

No files matched your search

+26 -33
View File
@@ -20,7 +20,7 @@ from enum import Enum
from typing import Annotated, List, Literal, Optional, Union
from uuid import UUID
from pydantic import AnyUrl, BaseModel, Discriminator, EmailStr, Field, Tag, model_validator
from pydantic import AnyUrl, BaseModel, Discriminator, EmailStr, Field, Tag
from ..common import ExtraConfig
@@ -599,45 +599,38 @@ class QemuPropertiesV8(BaseModel):
process_priority: Optional[QemuProcessPriority] = Field(None, title="Process priority for QEMU")
_V8_PROPERTIES_MODELS = {
TemplateType.qemu: QemuPropertiesV8,
TemplateType.dynamips: DynamipsPropertiesV8,
TemplateType.iou: IouPropertiesV8,
TemplateType.docker: DockerPropertiesV8,
}
class TemplateSetting(BaseModel):
class TemplateSettingBase(BaseModel):
"""Emulator settings configuration (v8)"""
name: Optional[str] = Field(None, title="Name of the settings set")
default: Optional[bool] = Field(None, title="Whether these are the default settings")
inherit_default_properties: Optional[bool] = Field(True, title="Whether the default properties should be used")
template_type: TemplateType = Field(..., title="Type of emulator properties")
template_properties: Union[QemuPropertiesV8, DynamipsPropertiesV8, IouPropertiesV8, DockerPropertiesV8] = Field(
..., title="Properties for the template"
)
@model_validator(mode="before")
@classmethod
def _validate_template_properties(cls, data):
"""
Validate template_properties against the model matching template_type.
The template_type discriminator lives at the settings level (not inside
template_properties), so the union cannot be discriminated by pydantic
alone and would misroute properties between the per-type models.
"""
if isinstance(data, dict):
# work on a copy: replacing template_properties with the validated
# model must not mutate the caller's data
data = data.copy()
template_type = data.get("template_type")
template_properties = data.get("template_properties")
model = _V8_PROPERTIES_MODELS.get(template_type)
if model is not None and isinstance(template_properties, dict):
data["template_properties"] = model.model_validate(template_properties)
return data
class QemuTemplateSetting(TemplateSettingBase):
template_type: Literal[TemplateType.qemu] = Field(..., title="Type of emulator properties")
template_properties: QemuPropertiesV8 = Field(..., title="Properties for the template")
class DynamipsTemplateSetting(TemplateSettingBase):
template_type: Literal[TemplateType.dynamips] = Field(..., title="Type of emulator properties")
template_properties: DynamipsPropertiesV8 = Field(..., title="Properties for the template")
class IouTemplateSetting(TemplateSettingBase):
template_type: Literal[TemplateType.iou] = Field(..., title="Type of emulator properties")
template_properties: IouPropertiesV8 = Field(..., title="Properties for the template")
class DockerTemplateSetting(TemplateSettingBase):
template_type: Literal[TemplateType.docker] = Field(..., title="Type of emulator properties")
template_properties: DockerPropertiesV8 = Field(..., title="Properties for the template")
TemplateSetting = Annotated[
Union[QemuTemplateSetting, DynamipsTemplateSetting, IouTemplateSetting, DockerTemplateSetting],
Field(discriminator="template_type"),
]
class ApplianceVersionV8(BaseModel):
+2 -1
View File
@@ -20,6 +20,7 @@ from uuid import UUID, uuid4
from pydantic import BaseModel, Field, field_validator
from ..update import PartialUpdateModel
from .labels import Label
@@ -73,7 +74,7 @@ class LinkCreate(LinkBase):
nodes: List[LinkNode] = Field(..., min_length=2, max_length=2)
class LinkUpdate(LinkBase):
class LinkUpdate(PartialUpdateModel, LinkBase):
pass
+3 -2
View File
@@ -21,6 +21,7 @@ from uuid import UUID, uuid4
from pydantic import BaseModel, Field
from ..common import ConsoleType, CustomAdapter, NodeStatus
from ..update import PartialUpdateModel
from .labels import Label
@@ -169,12 +170,12 @@ class NodeCreate(NodeBase):
node_id: UUID = Field(default_factory=uuid4)
class NodeUpdate(NodeBase):
class NodeUpdate(PartialUpdateModel, NodeBase):
"""
Data to update a node.
"""
pass
update_excluded_fields = ("node_id", "compute_id", "node_type")
class Node(NodeBase):
+4 -2
View File
@@ -21,6 +21,8 @@ from uuid import UUID
from pydantic import BaseModel, Field, HttpUrl
from ..update import PartialUpdateModel
class ProjectStatus(str, Enum):
"""
@@ -82,12 +84,12 @@ class ProjectDuplicate(ProjectBase):
reset_mac_addresses: Optional[bool] = Field(False, description="Reset MAC addresses for this project")
class ProjectUpdate(ProjectBase):
class ProjectUpdate(PartialUpdateModel, ProjectBase):
"""
Properties for project update.
"""
pass
update_excluded_fields = ("project_id", "path")
class Project(ProjectBase):
+2 -1
View File
@@ -70,7 +70,8 @@ class ACEUpdate(ACEBase):
Properties to update an ACE.
"""
pass
propagate: Optional[bool] = None
allowed: Optional[bool] = None
class ACE(DateTimeModelMixin, ACEBase):
@@ -20,6 +20,7 @@ from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field
from ...update import PartialUpdateModel
from ..base import DateTimeModelMixin
from ..nodes import NodeType
@@ -98,7 +99,8 @@ class TemplateCreate(TemplateBase):
model_config = ConfigDict(extra="allow")
class TemplateUpdate(TemplateBase):
class TemplateUpdate(PartialUpdateModel, TemplateBase):
update_excluded_fields = ("template_id",)
model_config = ConfigDict(extra="allow")
@@ -21,6 +21,7 @@ from pydantic import Field
from gns3server.schemas.compute.cloud_nodes import CloudConsoleType, EthernetPort, TAPPort, UDPPort
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -35,5 +36,5 @@ class CloudTemplate(TemplateBase):
remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface")
class CloudTemplateUpdate(CloudTemplate):
pass
class CloudTemplateUpdate(PartialUpdateModel, CloudTemplate):
update_excluded_fields = ("template_id",)
@@ -20,6 +20,7 @@ from typing import List, Optional
from pydantic import Field
from ...common import AuxType, ConsoleType, CustomAdapter, ExtraConfig
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -63,5 +64,5 @@ class DockerTemplate(DockerTemplateBase):
image: str = Field(..., description="Docker image name")
class DockerTemplateUpdate(DockerTemplateBase):
pass
class DockerTemplateUpdate(PartialUpdateModel, DockerTemplateBase):
update_excluded_fields = ("template_id",)
@@ -28,6 +28,7 @@ from gns3server.schemas.compute.dynamips_nodes import (
DynamipsWics,
)
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -73,7 +74,7 @@ class DynamipsTemplate(DynamipsTemplateBase):
image: str = Field(..., description="Path to the IOS image")
class C7200DynamipsTemplateUpdate(DynamipsTemplateBase):
class C7200DynamipsTemplateBase(DynamipsTemplateBase):
ram: Optional[int] = Field(512, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(512, gt=0, description="Amount of NVRAM in KB")
npe: Optional[DynamipsNPE] = Field(DynamipsNPE.npe_400, description="NPE model")
@@ -81,31 +82,43 @@ class C7200DynamipsTemplateUpdate(DynamipsTemplateBase):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C7200DynamipsTemplate(C7200DynamipsTemplateUpdate):
class C7200DynamipsTemplateUpdate(PartialUpdateModel, C7200DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C7200DynamipsTemplate(C7200DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
class C3725DynamipsTemplateUpdate(DynamipsTemplateBase):
class C3725DynamipsTemplateBase(DynamipsTemplateBase):
ram: Optional[int] = Field(128, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, gt=0, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(5, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3725DynamipsTemplate(C3725DynamipsTemplateUpdate):
class C3725DynamipsTemplateUpdate(PartialUpdateModel, C3725DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C3725DynamipsTemplate(C3725DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
class C3745DynamipsTemplateUpdate(DynamipsTemplateBase):
class C3745DynamipsTemplateBase(DynamipsTemplateBase):
ram: Optional[int] = Field(256, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, gt=0, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(5, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3745DynamipsTemplate(C3745DynamipsTemplateUpdate):
class C3745DynamipsTemplateUpdate(PartialUpdateModel, C3745DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C3745DynamipsTemplate(C3745DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
@@ -116,7 +129,7 @@ class C3600ChassisType(str, Enum):
chassis_3660 = "3660"
class C3600DynamipsTemplateUpdate(DynamipsTemplateBase):
class C3600DynamipsTemplateBase(DynamipsTemplateBase):
chassis: Optional[C3600ChassisType] = Field(C3600ChassisType.chassis_3660, description="Chassis type")
ram: Optional[int] = Field(192, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, gt=0, description="Amount of NVRAM in KB")
@@ -124,19 +137,27 @@ class C3600DynamipsTemplateUpdate(DynamipsTemplateBase):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3600DynamipsTemplate(C3600DynamipsTemplateUpdate):
class C3600DynamipsTemplateUpdate(PartialUpdateModel, C3600DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C3600DynamipsTemplate(C3600DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
class C2691DynamipsTemplateUpdate(DynamipsTemplateBase):
class C2691DynamipsTemplateBase(DynamipsTemplateBase):
ram: Optional[int] = Field(192, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, gt=0, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(5, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2691DynamipsTemplate(C2691DynamipsTemplateUpdate):
class C2691DynamipsTemplateUpdate(PartialUpdateModel, C2691DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C2691DynamipsTemplate(C2691DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
@@ -153,7 +174,7 @@ class C2600ChassisType(str, Enum):
chassis_2651xm = "2651XM"
class C2600DynamipsTemplateUpdate(DynamipsTemplateBase):
class C2600DynamipsTemplateBase(DynamipsTemplateBase):
chassis: Optional[C2600ChassisType] = Field(C2600ChassisType.chassis_2651xm, description="Chassis type")
ram: Optional[int] = Field(160, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, gt=0, description="Amount of NVRAM in KB")
@@ -161,7 +182,11 @@ class C2600DynamipsTemplateUpdate(DynamipsTemplateBase):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2600DynamipsTemplate(C2600DynamipsTemplateUpdate):
class C2600DynamipsTemplateUpdate(PartialUpdateModel, C2600DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C2600DynamipsTemplate(C2600DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
@@ -174,7 +199,7 @@ class C1700ChassisType(str, Enum):
chassis_1760 = "1760"
class C1700DynamipsTemplateUpdate(DynamipsTemplateBase):
class C1700DynamipsTemplateBase(DynamipsTemplateBase):
chassis: Optional[C1700ChassisType] = Field(C1700ChassisType.chassis_1760, description="Chassis type")
ram: Optional[int] = Field(160, gt=0, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, gt=0, description="Amount of NVRAM in KB")
@@ -182,6 +207,10 @@ class C1700DynamipsTemplateUpdate(DynamipsTemplateBase):
sparsemem: Optional[bool] = Field(False, description="Sparse memory feature")
class C1700DynamipsTemplate(C1700DynamipsTemplateUpdate):
class C1700DynamipsTemplateUpdate(PartialUpdateModel, C1700DynamipsTemplateBase):
update_excluded_fields = ("template_id",)
class C1700DynamipsTemplate(C1700DynamipsTemplateBase):
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
@@ -20,6 +20,7 @@ from pydantic import Field
from gns3server.schemas.compute.ethernet_hub_nodes import EthernetHubPort
from ...update import PartialUpdateModel
from . import Category, TemplateBase
DEFAULT_PORTS = [
@@ -41,5 +42,5 @@ class EthernetHubTemplate(TemplateBase):
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports")
class EthernetHubTemplateUpdate(EthernetHubTemplate):
pass
class EthernetHubTemplateUpdate(PartialUpdateModel, EthernetHubTemplate):
update_excluded_fields = ("template_id",)
@@ -22,6 +22,7 @@ from pydantic import Field
from gns3server.schemas.compute.ethernet_switch_nodes import EthernetSwitchPort
from ...update import PartialUpdateModel
from . import Category, TemplateBase
DEFAULT_PORTS = [
@@ -53,5 +54,5 @@ class EthernetSwitchTemplate(TemplateBase):
console_type: Optional[ConsoleType] = Field(ConsoleType.none, description="Console type")
class EthernetSwitchTemplateUpdate(EthernetSwitchTemplate):
pass
class EthernetSwitchTemplateUpdate(PartialUpdateModel, EthernetSwitchTemplate):
update_excluded_fields = ("template_id",)
@@ -21,6 +21,7 @@ from pydantic import Field
from gns3server.schemas.compute.iou_nodes import ConsoleType
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -50,5 +51,5 @@ class IOUTemplate(IOUTemplateBase):
path: str = Field(..., description="Path of IOU executable")
class IOUTemplateUpdate(IOUTemplateBase):
pass
class IOUTemplateUpdate(PartialUpdateModel, IOUTemplateBase):
update_excluded_fields = ("template_id",)
@@ -30,6 +30,7 @@ from gns3server.schemas.compute.qemu_nodes import (
QemuProcessPriority,
)
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -102,5 +103,5 @@ class QemuTemplate(TemplateBase):
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class QemuTemplateUpdate(QemuTemplate):
pass
class QemuTemplateUpdate(PartialUpdateModel, QemuTemplate):
update_excluded_fields = ("template_id",)
@@ -25,6 +25,7 @@ from gns3server.schemas.compute.virtualbox_nodes import (
VirtualBoxOnCloseAction,
)
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -65,5 +66,5 @@ class VirtualBoxTemplate(VirtualBoxTemplateBase):
vmname: str = Field(..., description="VirtualBox VM name (in VirtualBox itself)")
class VirtualBoxTemplateUpdate(VirtualBoxTemplateBase):
pass
class VirtualBoxTemplateUpdate(PartialUpdateModel, VirtualBoxTemplateBase):
update_excluded_fields = ("template_id",)
@@ -26,6 +26,7 @@ from gns3server.schemas.compute.vmware_nodes import (
VMwareOnCloseAction,
)
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -63,5 +64,5 @@ class VMwareTemplate(VMwareTemplateBase):
vmx_path: str = Field(..., description="Path to the vmx file")
class VMwareTemplateUpdate(VMwareTemplateBase):
pass
class VMwareTemplateUpdate(PartialUpdateModel, VMwareTemplateBase):
update_excluded_fields = ("template_id",)
@@ -21,6 +21,7 @@ from pydantic import Field
from gns3server.schemas.compute.vpcs_nodes import ConsoleType
from ...update import PartialUpdateModel
from . import Category, TemplateBase
@@ -35,5 +36,5 @@ class VPCSTemplate(TemplateBase):
)
class VPCSTemplateUpdate(VPCSTemplate):
pass
class VPCSTemplateUpdate(PartialUpdateModel, VPCSTemplate):
update_excluded_fields = ("template_id",)
+2 -1
View File
@@ -20,6 +20,7 @@ from uuid import UUID
from pydantic import BaseModel, ConfigDict, EmailStr, Field, SecretStr
from ..update import PartialUpdateModel
from .base import DateTimeModelMixin
@@ -43,7 +44,7 @@ class UserCreate(UserBase):
password: SecretStr = Field(..., min_length=8, max_length=100)
class UserUpdate(UserBase):
class UserUpdate(PartialUpdateModel, UserBase):
"""
Properties to update a user.
"""