diff --git a/gns3server/schemas/compute/vmware_nodes.py b/gns3server/schemas/compute/vmware_nodes.py index ee670a2bc..f838419a3 100644 --- a/gns3server/schemas/compute/vmware_nodes.py +++ b/gns3server/schemas/compute/vmware_nodes.py @@ -62,9 +62,9 @@ class VMwareBase(BaseModel): Common VMware node properties. """ - name: str - vmx_path: str = Field(..., description="Path to the vmx file") - linked_clone: bool = Field(..., description="Whether the VM is a linked clone or not") + name: Optional[str] = None + vmx_path: Optional[str] = None + linked_clone: Optional[bool] = None node_id: Optional[UUID] = None usage: Optional[str] = Field(None, description="How to use the node") console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port") @@ -83,7 +83,9 @@ class VMwareCreate(VMwareBase): Properties to create a VMware node. """ - pass + name: str + vmx_path: str = Field(..., description="Path to the vmx file") + linked_clone: bool = Field(..., description="Whether the VM is a linked clone or not") class VMwareUpdate(VMwareBase): @@ -91,12 +93,13 @@ class VMwareUpdate(VMwareBase): Properties to update a VMware node. """ - name: Optional[str] = None - vmx_path: Optional[str] = None - linked_clone: Optional[bool] = None + pass class VMware(VMwareBase): + name: str + vmx_path: str = Field(..., description="Path to the vmx file") + linked_clone: bool = Field(..., description="Whether the VM is a linked clone or not") project_id: UUID = Field(..., description="Project ID") node_directory: Optional[str] = Field(None, description="Path to the node working directory (read only)") status: NodeStatus = Field(..., description="Container status (read only)") diff --git a/gns3server/schemas/compute/vpcs_nodes.py b/gns3server/schemas/compute/vpcs_nodes.py index 30cd40960..fda0634c4 100644 --- a/gns3server/schemas/compute/vpcs_nodes.py +++ b/gns3server/schemas/compute/vpcs_nodes.py @@ -37,7 +37,7 @@ class VPCSBase(BaseModel): Common VPCS node properties. """ - name: str + name: Optional[str] = None node_id: Optional[UUID] = None usage: Optional[str] = Field(None, description="How to use the node") console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port") @@ -50,7 +50,7 @@ class VPCSCreate(VPCSBase): Properties to create a VPCS node. """ - pass + name: str class VPCSUpdate(VPCSBase): @@ -58,10 +58,11 @@ class VPCSUpdate(VPCSBase): Properties to update a VPCS node. """ - name: Optional[str] = None + pass class VPCS(VPCSBase): + name: str project_id: UUID = Field(..., description="Project ID") node_directory: str = Field(..., description="Path to the node working directory (read only)") status: NodeStatus = Field(..., description="Container status (read only)") diff --git a/gns3server/schemas/controller/appliances.py b/gns3server/schemas/controller/appliances.py index a4aecaa94..3660067b8 100644 --- a/gns3server/schemas/controller/appliances.py +++ b/gns3server/schemas/controller/appliances.py @@ -782,7 +782,7 @@ Appliance = ApplianceUnion # Create a validator wrapper for convenience from pydantic import TypeAdapter -_appliance_validator = TypeAdapter(ApplianceUnion) +_appliance_validator: TypeAdapter[Union[ApplianceV1_6, ApplianceV8]] = TypeAdapter(ApplianceUnion) class ApplianceModel: diff --git a/gns3server/schemas/controller/nodes.py b/gns3server/schemas/controller/nodes.py index 43d8ca5b4..9faf260d3 100644 --- a/gns3server/schemas/controller/nodes.py +++ b/gns3server/schemas/controller/nodes.py @@ -114,9 +114,9 @@ class NodeBase(BaseModel): Node data. """ - compute_id: Union[UUID, str] - name: str - node_type: NodeType + compute_id: Optional[Union[UUID, str]] = None + name: Optional[str] = None + node_type: Optional[NodeType] = None node_id: Optional[UUID] = None @@ -162,6 +162,9 @@ class NodeBase(BaseModel): class NodeCreate(NodeBase): + compute_id: Union[UUID, str] + name: str + node_type: NodeType node_id: UUID = Field(default_factory=uuid4) @@ -170,12 +173,13 @@ class NodeUpdate(NodeBase): Data to update a node. """ - compute_id: Optional[Union[UUID, str]] = None - name: Optional[str] = None - node_type: Optional[NodeType] = None + pass class Node(NodeBase): + compute_id: Union[UUID, str] + name: str + node_type: NodeType template_id: Optional[UUID] = Field( None, description="Template UUID from which the node has been created. Read only" ) diff --git a/gns3server/schemas/controller/projects.py b/gns3server/schemas/controller/projects.py index ddfbde02e..e1210f99f 100644 --- a/gns3server/schemas/controller/projects.py +++ b/gns3server/schemas/controller/projects.py @@ -45,7 +45,7 @@ class ProjectBase(BaseModel): Common properties for projects. """ - name: str + name: Optional[str] = None project_id: Optional[UUID] = None path: Optional[str] = Field(None, description="Project directory") auto_close: Optional[bool] = Field(None, description="Close project when last client leaves") @@ -69,7 +69,7 @@ class ProjectCreate(ProjectBase): Properties for project creation. """ - pass + name: str class ProjectDuplicate(ProjectBase): @@ -77,6 +77,7 @@ class ProjectDuplicate(ProjectBase): Properties for project duplication. """ + name: str reset_mac_addresses: Optional[bool] = Field(False, description="Reset MAC addresses for this project") @@ -85,12 +86,11 @@ class ProjectUpdate(ProjectBase): Properties for project update. """ - name: Optional[str] = None + pass class Project(ProjectBase): project_id: UUID - name: Optional[str] = None status: Optional[ProjectStatus] = None filename: Optional[str] = None created_by: Optional[str] = Field(None, description="Username of the user who created the project") diff --git a/gns3server/schemas/controller/settings.py b/gns3server/schemas/controller/settings.py index d64d029e1..9925c403c 100644 --- a/gns3server/schemas/controller/settings.py +++ b/gns3server/schemas/controller/settings.py @@ -25,6 +25,7 @@ says, so exposing or writing it via the API would be useless at best and a secret leak at worst. """ +from pathlib import Path from typing import List, Optional from pydantic import ConfigDict, BaseModel, Field @@ -49,9 +50,11 @@ SECRET_MASK = "**********" class ServerSettingsResponse(ServerSettings): # plain strings instead of FilePath/DirectoryPath: paths are validated when # the settings are loaded or updated, not when echoed back to the client - secrets_dir: Optional[str] = Field(None, description="Directory where secrets are stored (e.g. the JWT secret key)") - certfile: Optional[str] = Field(None, description="SSL certificate file, requires enable_ssl") - certkey: Optional[str] = Field(None, description="SSL key file, requires enable_ssl") + secrets_dir: Optional[Path] = Field( + None, description="Directory where secrets are stored (e.g. the JWT secret key)" + ) + certfile: Optional[Path] = Field(None, description="SSL certificate file, requires enable_ssl") + certkey: Optional[Path] = Field(None, description="SSL key file, requires enable_ssl") # Optional overrides: typed as plain "str = None" in the config schema, # which fails re-validation when the value actually is None resources_path: Optional[str] = Field( diff --git a/gns3server/schemas/controller/templates/docker_templates.py b/gns3server/schemas/controller/templates/docker_templates.py index 1c7a320e3..4123630e8 100644 --- a/gns3server/schemas/controller/templates/docker_templates.py +++ b/gns3server/schemas/controller/templates/docker_templates.py @@ -22,11 +22,11 @@ from pydantic import Field from typing import Optional, List -class DockerTemplate(TemplateBase): +class DockerTemplateBase(TemplateBase): category: Optional[Category] = Category.guest default_name_format: Optional[str] = "{name}-{0}" symbol: Optional[str] = "docker_guest" - image: str = Field(..., description="Docker image name") + image: Optional[str] = Field(None, description="Docker image name") adapters: Optional[int] = Field(1, ge=0, le=100, description="Number of adapters") mac_address: Optional[str] = Field( "", description="Base MAC address", pattern="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$|^$" @@ -58,5 +58,9 @@ class DockerTemplate(TemplateBase): custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") -class DockerTemplateUpdate(DockerTemplate): - image: Optional[str] = Field(None, description="Docker image name") +class DockerTemplate(DockerTemplateBase): + image: str = Field(..., description="Docker image name") + + +class DockerTemplateUpdate(DockerTemplateBase): + pass diff --git a/pyproject.toml b/pyproject.toml index ac204cfd0..9aa5fe242 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -312,14 +312,7 @@ module = [ "gns3server.db.repositories.templates", # 6 "gns3server.db.repositories.users", # 12 "gns3server.db.tasks", # 12 - "gns3server.schemas.compute.vmware_nodes", # 3 - "gns3server.schemas.compute.vpcs_nodes", # 1 - "gns3server.schemas.controller.appliances", # 1 "gns3server.schemas.controller.computes", # 6 - "gns3server.schemas.controller.nodes", # 3 - "gns3server.schemas.controller.projects", # 2 - "gns3server.schemas.controller.settings", # 3 - "gns3server.schemas.controller.templates.docker_templates", # 1 "gns3server.schemas.controller.templates.iou_templates", # 1 "gns3server.schemas.controller.templates.virtualbox_templates", # 1 "gns3server.schemas.controller.templates.vmware_templates", # 1