1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 16:58:28 +00:00

Use pydantic for data validation (instead of jsonschema)

Fix/improve various pydantic shema models.
This commit is contained in:
grossmj 2020-10-31 15:07:12 +10:30
parent d6a65883ee
commit 81439c750a
29 changed files with 784 additions and 350 deletions

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .vpcs_templates import VPCSTemplate
from .cloud_templates import CloudTemplate
from .iou_templates import IOUTemplate
from .docker_templates import DockerTemplate
from .ethernet_hub_templates import EthernetHubTemplate
from .ethernet_switch_templates import EthernetSwitchTemplate
from .virtualbox_templates import VirtualBoxTemplate
from .vmware_templates import VMwareTemplate
from .qemu_templates import QemuTemplate
from .dynamips_templates import (
DynamipsTemplate,
C1700DynamipsTemplate,
C2600DynamipsTemplate,
C2691DynamipsTemplate,
C3600DynamipsTemplate,
C3725DynamipsTemplate,
C3745DynamipsTemplate,
C7200DynamipsTemplate
)

View File

@ -16,30 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from .templates import Category, TemplateBase from gns3server.endpoints.schemas.templates import Category, TemplateBase
from .cloud_nodes import EthernetPort, TAPPort, UDPPort from gns3server.endpoints.schemas.cloud_nodes import EthernetPort, TAPPort, UDPPort, CloudConsoleType
from pydantic import Field from pydantic import Field
from typing import Optional, Union, List from typing import Optional, Union, List
from enum import Enum
from .nodes import NodeType
class RemoteConsoleType(str, Enum): class CloudTemplate(TemplateBase):
"""
Supported remote console types for cloud nodes.
"""
none = "none"
telnet = "telnet"
vnc = "vnc"
spice = "spice"
http = "http"
https = "https"
class CloudTemplateBase(TemplateBase):
category: Optional[Category] = "guest" category: Optional[Category] = "guest"
default_name_format: Optional[str] = "Cloud{0}" default_name_format: Optional[str] = "Cloud{0}"
@ -47,28 +31,5 @@ class CloudTemplateBase(TemplateBase):
ports_mapping: List[Union[EthernetPort, TAPPort, UDPPort]] = [] ports_mapping: List[Union[EthernetPort, TAPPort, UDPPort]] = []
remote_console_host: Optional[str] = Field("127.0.0.1", description="Remote console host or IP") remote_console_host: Optional[str] = Field("127.0.0.1", description="Remote console host or IP")
remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port") remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port")
remote_console_type: Optional[RemoteConsoleType] = Field("none", description="Remote console type") remote_console_type: Optional[CloudConsoleType] = Field("none", description="Remote console type")
remote_console_path: Optional[str] = Field("/", description="Path of the remote web interface") remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface")
class CloudTemplateCreate(CloudTemplateBase):
name: str
template_type: NodeType
compute_id: str
class CloudTemplateUpdate(CloudTemplateBase):
pass
class CloudTemplate(CloudTemplateBase):
template_id: str
name: str
category: Category
symbol: str
builtin: bool
template_type: NodeType
compute_id: Union[str, None]

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.nodes import CustomAdapter
from gns3server.endpoints.schemas.docker_nodes import ConsoleType, AuxType
from pydantic import Field
from typing import Optional, List
class DockerTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/docker_guest.svg"
image: str = Field(..., description="Docker image name")
adapters: Optional[int] = Field(1, ge=0, le=100, description="Number of adapters")
start_command: Optional[str] = Field("", description="Docker CMD entry")
environment: Optional[str] = Field("", description="Docker environment variables")
console_type: Optional[ConsoleType] = Field("telnet", description="Console type")
aux_type: Optional[AuxType] = Field("none", description="Auxiliary console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
console_http_port: Optional[int] = Field(80, gt=0, le=65535, description="Internal port in the container for the HTTP server")
console_http_path: Optional[str] = Field("/", description="Path of the web interface",)
console_resolution: Optional[str] = Field("1024x768", regex="^[0-9]+x[0-9]+$", description="Console resolution for VNC")
extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)")
extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent")
memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB")
cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field([], description="Custom adapters")

View File

@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.dynamips_nodes import (
DynamipsConsoleType,
DynamipsPlatform,
DynamipsAdapters,
DynamipsWics,
DynamipsNPE,
DynamipsMidplane
)
from pydantic import Field
from pathlib import Path
from typing import Optional
from enum import Enum
class DynamipsTemplate(TemplateBase):
category: Optional[Category] = "router"
default_name_format: Optional[str] = "R{0}"
symbol: Optional[str] = ":/symbols/router.svg"
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: Path = Field(..., description="Path to the IOS image")
exec_area: Optional[int] = Field(64, description="Exec area value")
mmap: Optional[bool] = Field(True, description="MMAP feature")
mac_addr: Optional[str] = Field("", description="Base MAC address", regex="^([0-9a-fA-F]{4}\\.){2}[0-9a-fA-F]{4}$|^$")
system_id: Optional[str] = Field("FTX0945W0MY", description="System ID")
startup_config: Optional[str] = Field("ios_base_startup-config.txt", description="IOS startup configuration file")
private_config: Optional[str] = Field("", description="IOS private configuration file")
idlepc: Optional[str] = Field("", description="Idle-PC value", regex="^(0x[0-9a-fA-F]+)?$|^$")
idlemax: Optional[int] = Field(500, description="Idlemax value")
idlesleep: Optional[int] = Field(30, description="Idlesleep value")
disk0: Optional[int] = Field(0, description="Disk0 size in MB")
disk1: Optional[int] = Field(0, description="Disk1 size in MB")
auto_delete_disks: Optional[bool] = Field(False, description="Automatically delete nvram and disk files")
console_type: Optional[DynamipsConsoleType] = Field("telnet", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
aux_type: Optional[DynamipsConsoleType] = Field("none", description="Auxiliary console type")
slot0: Optional[DynamipsAdapters] = Field(None, description="Network module slot 0")
slot1: Optional[DynamipsAdapters] = Field(None, description="Network module slot 1")
slot2: Optional[DynamipsAdapters] = Field(None, description="Network module slot 2")
slot3: Optional[DynamipsAdapters] = Field(None, description="Network module slot 3")
slot4: Optional[DynamipsAdapters] = Field(None, description="Network module slot 4")
slot5: Optional[DynamipsAdapters] = Field(None, description="Network module slot 5")
slot6: Optional[DynamipsAdapters] = Field(None, description="Network module slot 6")
wic0: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 0")
wic1: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 1")
wic2: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 2")
class C7200DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(512, description="Amount of RAM in MB")
nvram: Optional[int] = Field(512, description="Amount of NVRAM in KB")
npe: Optional[DynamipsNPE] = Field("npe-400", description="NPE model")
midplane: Optional[DynamipsMidplane] = Field("vxr", description="Midplane model")
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3725DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(128, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, 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(DynamipsTemplate):
ram: Optional[int] = Field(256, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, 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 C3600ChassisType(str, Enum):
chassis_3620 = "3620"
chassis_3640 = "3640"
chassis_3660 = "3660"
class C3600DynamipsTemplate(DynamipsTemplate):
chassis: Optional[C3600ChassisType] = Field("c3660", description="Chassis type")
ram: Optional[int] = Field(192, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, 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(DynamipsTemplate):
ram: Optional[int] = Field(192, description="Amount of RAM in MB")
nvram: Optional[int] = Field(256, 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 C2600ChassisType(str, Enum):
chassis_2610 = "2610"
chassis_2620 = "2620"
chassis_2610xm = "2610XM"
chassis_2620xm = "2620XM"
chassis_2650xm = "2650XM"
chassis_2621 = "2621"
chassis_2611xm = "2611XM"
chassis_2621xm = "2621XM"
chassis_2651xm = "2651XM"
class C2600DynamipsTemplate(DynamipsTemplate):
chassis: Optional[C2600ChassisType] = Field("2651XM", description="Chassis type")
ram: Optional[int] = Field(160, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C1700ChassisType(str, Enum):
chassis_1720 = "1720"
chassis_1721 = "1721"
chassis_1750 = "1750"
chassis_1751 = "1751"
chassis_1760 = "1760"
class C1700DynamipsTemplate(DynamipsTemplate):
chassis: Optional[C1700ChassisType] = Field("1760", description="Chassis type")
ram: Optional[int] = Field(160, description="Amount of RAM in MB")
nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(False, description="Sparse memory feature")

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.ethernet_hub_nodes import EthernetHubPort
from pydantic import Field
from typing import Optional, List
DEFAULT_PORTS = [
dict(port_number=0, name="Ethernet0"),
dict(port_number=1, name="Ethernet1"),
dict(port_number=2, name="Ethernet2"),
dict(port_number=3, name="Ethernet3"),
dict(port_number=4, name="Ethernet4"),
dict(port_number=5, name="Ethernet5"),
dict(port_number=6, name="Ethernet6"),
dict(port_number=7, name="Ethernet7")
]
class EthernetHubTemplate(TemplateBase):
category: Optional[Category] = "switch"
default_name_format: Optional[str] = "Hub{0}"
symbol: Optional[str] = ":/symbols/hub.svg"
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports")

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.ethernet_switch_nodes import EthernetSwitchPort
from pydantic import Field
from typing import Optional, List
from enum import Enum
DEFAULT_PORTS = [
dict(port_number=0, name="Ethernet0", vlan=1, type="access", ethertype=""),
dict(port_number=1, name="Ethernet1", vlan=1, type="access", ethertype=""),
dict(port_number=2, name="Ethernet2", vlan=1, type="access", ethertype=""),
dict(port_number=3, name="Ethernet3", vlan=1, type="access", ethertype=""),
dict(port_number=4, name="Ethernet4", vlan=1, type="access", ethertype=""),
dict(port_number=5, name="Ethernet5", vlan=1, type="access", ethertype=""),
dict(port_number=6, name="Ethernet6", vlan=1, type="access", ethertype=""),
dict(port_number=7, name="Ethernet7", vlan=1, type="access", ethertype="")
]
class ConsoleType(str, Enum):
"""
Supported console types for Ethernet switch nodes.
"""
none = "none"
telnet = "telnet"
class EthernetSwitchTemplate(TemplateBase):
category: Optional[Category] = "switch"
default_name_format: Optional[str] = "Switch{0}"
symbol: Optional[str] = ":/symbols/ethernet_switch.svg"
ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports")
console_type: Optional[ConsoleType] = Field("none", description="Console type")

View File

@ -16,31 +16,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from .templates import Category, TemplateBase from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.iou_nodes import ConsoleType
from pydantic import Field from pydantic import Field
from pathlib import Path from pathlib import Path
from typing import Optional, Union from typing import Optional
from enum import Enum
from .nodes import NodeType
class ConsoleType(str, Enum): class IOUTemplate(TemplateBase):
"""
Supported console types for IOU nodes
"""
none = "none"
telnet = "telnet"
class IOUTemplateBase(TemplateBase):
category: Optional[Category] = "router" category: Optional[Category] = "router"
default_name_format: Optional[str] = "IOU{0}" default_name_format: Optional[str] = "IOU{0}"
symbol: Optional[str] = ":/symbols/multilayer_switch.svg" symbol: Optional[str] = ":/symbols/multilayer_switch.svg"
path: Path = Field(..., description="Path of IOU executable") path: Path = Field(..., description="Path of IOU executable")
ethernet_adapters: Optional[int] = Field(2, description="Number of ethernet adapters") ethernet_adapters: Optional[int] = Field(2, description="Number of ethernet adapters")
serial_adapters: Optional[int] = Field(2, description="Number of serial adapters") serial_adapters: Optional[int] = Field(2, description="Number of serial adapters")
@ -53,25 +41,3 @@ class IOUTemplateBase(TemplateBase):
console_type: Optional[ConsoleType] = Field("telnet", description="Console type") console_type: Optional[ConsoleType] = Field("telnet", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started") console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
class IOUTemplateCreate(IOUTemplateBase):
name: str
template_type: NodeType
compute_id: str
class IOUTemplateUpdate(IOUTemplateBase):
pass
class IOUTemplate(IOUTemplateBase):
template_id: str
name: str
category: Category
symbol: str
builtin: bool
template_type: NodeType
compute_id: Union[str, None]

View File

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.qemu_nodes import (
QemuConsoleType,
QemuPlatform,
QemuAdapterType,
QemuOnCloseAction,
QemuBootPriority,
QemuDiskInterfaceType,
QemuProcessPriority,
CustomAdapter
)
from pathlib import Path
from pydantic import Field
from typing import Optional, List
class QemuTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/qemu_guest.svg"
qemu_path: Optional[Path] = Field("", description="Qemu executable path")
platform: Optional[QemuPlatform] = Field("i386", description="Platform to emulate")
linked_clone: Optional[bool] = Field(True, description="Whether the VM is a linked clone or not")
ram: Optional[int] = Field(256, description="Amount of RAM in MB")
cpus: Optional[int] = Field(1, ge=1, le=255, description="Number of vCPUs")
maxcpus: Optional[int] = Field(1, ge=1, le=255, description="Maximum number of hotpluggable vCPUs")
adapters: Optional[int] = Field(1, ge=0, le=275, description="Number of adapters")
adapter_type: Optional[QemuAdapterType] = Field("e1000", description="QEMU adapter type")
mac_address: Optional[str] = Field("", description="QEMU MAC address", regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$|^$")
first_port_name: Optional[str] = Field("", description="Optional name of the first networking port example: eth0")
port_name_format: Optional[str] = Field("Ethernet{0}", description="Optional formatting of the networking port example: eth{0}")
port_segment_size: Optional[int] = Field(0, description="Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2")
console_type: Optional[QemuConsoleType] = Field("telnet", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
aux_type: Optional[QemuConsoleType] = Field("none", description="Auxiliary console type")
boot_priority: Optional[QemuBootPriority] = Field("c", description="QEMU boot priority")
hda_disk_image: Optional[Path] = Field("", description="QEMU hda disk image path")
hda_disk_interface: Optional[QemuDiskInterfaceType] = Field("none", description="QEMU hda interface")
hdb_disk_image: Optional[Path] = Field("", description="QEMU hdb disk image path")
hdb_disk_interface: Optional[QemuDiskInterfaceType] = Field("none", description="QEMU hdb interface")
hdc_disk_image: Optional[Path] = Field("", description="QEMU hdc disk image path")
hdc_disk_interface: Optional[QemuDiskInterfaceType] = Field("none", description="QEMU hdc interface")
hdd_disk_image: Optional[Path] = Field("", description="QEMU hdd disk image path")
hdd_disk_interface: Optional[QemuDiskInterfaceType] = Field("none", description="QEMU hdd interface")
cdrom_image: Optional[Path] = Field("", description="QEMU cdrom image path")
initrd: Optional[Path] = Field("", description="QEMU initrd path")
kernel_image: Optional[Path] = Field("", description="QEMU kernel image path")
bios_image: Optional[Path] = Field("", description="QEMU bios image path")
kernel_command_line: Optional[str] = Field("", description="QEMU kernel command line")
legacy_networking: Optional[bool] = Field(False, description="Use QEMU legagy networking commands (-net syntax)")
replicate_network_connection_state: Optional[bool] = Field(True, description="Replicate the network connection state for links in Qemu")
create_config_disk: Optional[bool] = Field(False, description="Automatically create a config disk on HDD disk interface (secondary slave)")
on_close: Optional[QemuOnCloseAction] = Field("power_off", description="Action to execute on the VM is closed")
cpu_throttling: Optional[int] = Field(0, ge=0, le=800, description="Percentage of CPU allowed for QEMU")
process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU")
options: Optional[str] = Field("", description="Additional QEMU options")
custom_adapters: Optional[List[CustomAdapter]] = Field([], description="Custom adapters")

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This file contains the validation for checking a .gns3 file
#
from gns3server.endpoints.schemas.computes import Compute
from gns3server.endpoints.schemas.drawings import Drawing
from gns3server.endpoints.schemas.links import Link
from gns3server.endpoints.schemas.nodes import Node
from gns3server.endpoints.schemas.projects import (
Supplier,
Variable
)
from pydantic import BaseModel, Field
from typing import Optional, List
from enum import Enum
from uuid import UUID
class TopologyType(str, Enum):
topology = "topology"
class TopologyContent(BaseModel):
computes: List[Compute] = Field(..., description="List of computes")
drawings: List[Drawing] = Field(..., description="List of drawings")
links: List[Link] = Field(..., description="List of links")
nodes: List[Node] = Field(..., description="List of nodes")
class Topology(BaseModel):
project_id: UUID = Field(..., description="Project UUID")
type: TopologyType = Field(..., description="Type of file. It's always topology")
revision: int = Field(..., description="Version of the .gns3 specification")
version: str = Field(..., description="Version of the GNS3 software which have update the file for the last time")
name: str = Field(..., description="Name of the project")
topology: TopologyContent = Field(..., description="Topology content")
auto_start: Optional[bool] = Field(None, description="Start the topology when opened")
auto_close: Optional[bool] = Field(None, description="Close the topology when no client is connected")
scene_height: Optional[int] = Field(None, description="Height of the drawing area")
scene_width: Optional[int] = Field(None, description="Width of the drawing area")
zoom: Optional[int] = Field(None, description="Zoom of the drawing area")
show_layers: Optional[bool] = Field(None, description="Show layers on the drawing area")
snap_to_grid: Optional[bool] = Field(None, description="Snap to grid on the drawing area")
show_grid: Optional[bool] = Field(None, description="Show the grid on the drawing area")
grid_size: Optional[int] = Field(None, description="Grid size for the drawing area for nodes")
drawing_grid_size: Optional[int] = Field(None, description="Grid size for the drawing area for drawings")
show_interface_labels: Optional[bool] = Field(None, description="Show interface labels on the drawing area")
supplier: Optional[Supplier] = Field(None, description="Supplier of the project")
variables: Optional[List[Variable]] = Field(None, description="Variables required to run the project")
def main():
import json
import sys
with open(sys.argv[1]) as f:
data = json.load(f)
Topology.parse_obj(data)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.virtualbox_nodes import (
VirtualBoxConsoleType,
VirtualBoxAdapterType,
VirtualBoxOnCloseAction,
CustomAdapter
)
from pydantic import Field
from typing import Optional, List
class VirtualBoxTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/vbox_guest.svg"
vmname: str = Field(..., description="VirtualBox VM name (in VirtualBox itself)")
ram: Optional[int] = Field(256, gt=0, description="Amount of RAM in MB")
linked_clone: Optional[bool] = Field(False, description="Whether the VM is a linked clone or not")
adapters: Optional[int] = Field(1, ge=0, le=36, description="Number of adapters") # 36 is the maximum given by the ICH9 chipset in VirtualBox
use_any_adapter: Optional[bool] = Field(False, description="Allow GNS3 to use any VirtualBox adapter")
adapter_type: Optional[VirtualBoxAdapterType] = Field("Intel PRO/1000 MT Desktop (82540EM)", description="VirtualBox adapter type")
first_port_name: Optional[str] = Field("", description="Optional name of the first networking port example: eth0")
port_name_format: Optional[str] = Field("Ethernet{0}", description="Optional formatting of the networking port example: eth{0}")
port_segment_size: Optional[int] = Field(0, description="Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2")
headless: Optional[bool] = Field(False, description="Headless mode")
on_close: Optional[VirtualBoxOnCloseAction] = Field("power_off", description="Action to execute on the VM is closed")
console_type: Optional[VirtualBoxConsoleType] = Field("none", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
custom_adapters: Optional[List[CustomAdapter]] = Field([], description="Custom adapters")

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.vmware_nodes import (
VMwareConsoleType,
VMwareAdapterType,
VMwareOnCloseAction,
CustomAdapter
)
from pathlib import Path
from pydantic import Field
from typing import Optional, List
class VMwareTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/vmware_guest.svg"
vmx_path: Path = Field(..., description="Path to the vmx file")
linked_clone: Optional[bool] = Field(False, description="Whether the VM is a linked clone or not")
first_port_name: Optional[str] = Field("", description="Optional name of the first networking port example: eth0")
port_name_format: Optional[str] = Field("Ethernet{0}", description="Optional formatting of the networking port example: eth{0}")
port_segment_size: Optional[int] = Field(0, description="Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2")
adapters: Optional[int] = Field(1, ge=0, le=10, description="Number of adapters") # 10 is the maximum adapters support by VMware VMs
adapter_type: Optional[VMwareAdapterType] = Field("e1000", description="VMware adapter type")
use_any_adapter: Optional[bool] = Field(False, description="Allow GNS3 to use any VMware adapter")
headless: Optional[bool] = Field(False, description="Headless mode")
on_close: Optional[VMwareOnCloseAction] = Field("power_off", description="Action to execute on the VM is closed")
console_type: Optional[VMwareConsoleType] = Field("none", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
custom_adapters: Optional[List[CustomAdapter]] = Field([], description="Custom adapters")

View File

@ -16,53 +16,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from .templates import Category, TemplateBase from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.vpcs_nodes import ConsoleType
from pydantic import Field from pydantic import Field
from typing import Optional, Union from typing import Optional
from enum import Enum
from .nodes import NodeType
class ConsoleType(str, Enum): class VPCSTemplate(TemplateBase):
"""
Supported console types for VPCS nodes
"""
none = "none"
telnet = "telnet"
class VPCSTemplateBase(TemplateBase):
category: Optional[Category] = "guest" category: Optional[Category] = "guest"
default_name_format: Optional[str] = "PC{0}" default_name_format: Optional[str] = "PC{0}"
symbol: Optional[str] = ":/symbols/vpcs_guest.svg" symbol: Optional[str] = ":/symbols/vpcs_guest.svg"
base_script_file: Optional[str] = Field("vpcs_base_config.txt", description="Script file") base_script_file: Optional[str] = Field("vpcs_base_config.txt", description="Script file")
console_type: Optional[ConsoleType] = Field("telnet", description="Console type") console_type: Optional[ConsoleType] = Field("telnet", description="Console type")
console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started") console_auto_start: Optional[bool] = Field(False, description="Automatically start the console when the node has started")
class VPCSTemplateCreate(VPCSTemplateBase):
name: str
template_type: NodeType
compute_id: str
class VPCSTemplateUpdate(VPCSTemplateBase):
pass
class VPCSTemplate(VPCSTemplateBase):
template_id: str
name: str
category: Category
symbol: str
builtin: bool
template_type: NodeType
compute_id: Union[str, None]

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -17,57 +17,14 @@
import copy import copy
import uuid import uuid
import json
import jsonschema
from gns3server.schemas.cloud_template import CLOUD_TEMPLATE_OBJECT_SCHEMA from pydantic import ValidationError
from gns3server.schemas.ethernet_switch_template import ETHERNET_SWITCH_TEMPLATE_OBJECT_SCHEMA from fastapi.encoders import jsonable_encoder
from gns3server.schemas.ethernet_hub_template import ETHERNET_HUB_TEMPLATE_OBJECT_SCHEMA from gns3server.controller import schemas
from gns3server.schemas.docker_template import DOCKER_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.vpcs_template import VPCS_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.traceng_template import TRACENG_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.virtualbox_template import VIRTUALBOX_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.vmware_template import VMWARE_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.iou_template import IOU_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.qemu_template import QEMU_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.dynamips_template import (
DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C7200_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C3745_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C3725_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C3600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C2691_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C2600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
C1700_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA
)
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# Add default values for missing entries in a request, largely taken from jsonschema documentation example
# https://python-jsonschema.readthedocs.io/en/latest/faq/#why-doesn-t-my-schema-s-default-property-set-the-default-on-my-instance
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
if jsonschema.Draft4Validator(schema).is_valid(instance):
# only add default for the matching sub-schema (e.g. when using 'oneOf')
for property, subschema in properties.items():
if "default" in subschema:
instance.setdefault(property, subschema["default"])
for error in validate_properties(validator, properties, instance, schema,):
yield error
return jsonschema.validators.extend(
validator_class, {"properties" : set_defaults},
)
ValidatorWithDefaults = extend_with_default(jsonschema.Draft4Validator)
ID_TO_CATEGORY = { ID_TO_CATEGORY = {
3: "firewall", 3: "firewall",
2: "guest", 2: "guest",
@ -76,27 +33,26 @@ ID_TO_CATEGORY = {
} }
TEMPLATE_TYPE_TO_SHEMA = { TEMPLATE_TYPE_TO_SHEMA = {
"cloud": CLOUD_TEMPLATE_OBJECT_SCHEMA, "cloud": schemas.CloudTemplate,
"ethernet_hub": ETHERNET_HUB_TEMPLATE_OBJECT_SCHEMA, "ethernet_hub": schemas.EthernetHubTemplate,
"ethernet_switch": ETHERNET_SWITCH_TEMPLATE_OBJECT_SCHEMA, "ethernet_switch": schemas.EthernetSwitchTemplate,
"docker": DOCKER_TEMPLATE_OBJECT_SCHEMA, "docker": schemas.DockerTemplate,
"dynamips": DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "dynamips": schemas.DynamipsTemplate,
"vpcs": VPCS_TEMPLATE_OBJECT_SCHEMA, "vpcs": schemas.VPCSTemplate,
"traceng": TRACENG_TEMPLATE_OBJECT_SCHEMA, "virtualbox": schemas.VirtualBoxTemplate,
"virtualbox": VIRTUALBOX_TEMPLATE_OBJECT_SCHEMA, "vmware": schemas.VMwareTemplate,
"vmware": VMWARE_TEMPLATE_OBJECT_SCHEMA, "iou": schemas.IOUTemplate,
"iou": IOU_TEMPLATE_OBJECT_SCHEMA, "qemu": schemas.QemuTemplate
"qemu": QEMU_TEMPLATE_OBJECT_SCHEMA
} }
DYNAMIPS_PLATFORM_TO_SHEMA = { DYNAMIPS_PLATFORM_TO_SHEMA = {
"c7200": C7200_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c7200": schemas.C7200DynamipsTemplate,
"c3745": C3745_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c3745": schemas.C3745DynamipsTemplate,
"c3725": C3725_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c3725": schemas.C3725DynamipsTemplate,
"c3600": C3600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c3600": schemas.C3600DynamipsTemplate,
"c2691": C2691_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c2691": schemas.C2691DynamipsTemplate,
"c2600": C2600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA, "c2600": schemas.C2600DynamipsTemplate,
"c1700": C1700_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA "c1700": schemas.C1700DynamipsTemplate
} }
@ -141,11 +97,18 @@ class Template:
self._builtin = builtin self._builtin = builtin
if builtin is False: if builtin is False:
self.validate_and_apply_defaults(TEMPLATE_TYPE_TO_SHEMA[self.template_type]) try:
template_schema = TEMPLATE_TYPE_TO_SHEMA[self.template_type]
template_settings_with_defaults = template_schema .parse_obj(self.__json__())
self._settings = jsonable_encoder(template_settings_with_defaults.dict())
if self.template_type == "dynamips": if self.template_type == "dynamips":
# special case for Dynamips to cover all platform types that contain specific settings # special case for Dynamips to cover all platform types that contain specific settings
self.validate_and_apply_defaults(DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]]) dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]]
dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(self.__json__())
self._settings = jsonable_encoder(dynamips_template_settings_with_defaults.dict())
except ValidationError as e:
print(e) #TODO: handle errors
raise
log.debug('Template "{name}" [{id}] loaded'.format(name=self.name, id=self._id)) log.debug('Template "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
@ -179,7 +142,6 @@ class Template:
def update(self, **kwargs): def update(self, **kwargs):
from gns3server.controller import Controller from gns3server.controller import Controller
controller = Controller.instance() controller = Controller.instance()
Controller.instance().check_can_write_config() Controller.instance().check_can_write_config()
@ -187,17 +149,6 @@ class Template:
controller.notification.controller_emit("template.updated", self.__json__()) controller.notification.controller_emit("template.updated", self.__json__())
controller.save() controller.save()
def validate_and_apply_defaults(self, schema):
validator = ValidatorWithDefaults(schema)
try:
validator.validate(self.__json__())
except jsonschema.ValidationError as e:
message = "JSON schema error {}".format(e.message)
log.error(message)
log.debug("Input schema: {}".format(json.dumps(schema)))
raise
def __json__(self): def __json__(self):
""" """
Template settings. Template settings.

View File

@ -17,7 +17,7 @@
import copy import copy
import uuid import uuid
import jsonschema import pydantic
from .controller_error import ControllerError, ControllerNotFoundError from .controller_error import ControllerError, ControllerNotFoundError
from .template import Template from .template import Template
@ -53,8 +53,8 @@ class TemplateManager:
try: try:
template = Template(template_settings.get("template_id"), template_settings) template = Template(template_settings.get("template_id"), template_settings)
self._templates[template.id] = template self._templates[template.id] = template
except jsonschema.ValidationError as e: except pydantic.ValidationError as e:
message = "Cannot load template with JSON data '{}': {}".format(template_settings, e.message) message = "Cannot load template with JSON data '{}': {}".format(template_settings, e)
log.warning(message) log.warning(message)
continue continue
@ -90,8 +90,8 @@ class TemplateManager:
template_id = settings.setdefault("template_id", str(uuid.uuid4())) template_id = settings.setdefault("template_id", str(uuid.uuid4()))
try: try:
template = Template(template_id, settings) template = Template(template_id, settings)
except jsonschema.ValidationError as e: except pydantic.ValidationError as e:
message = "JSON schema error adding template with JSON data '{}': {}".format(settings, e.message) message = "JSON schema error adding template with JSON data '{}': {}".format(settings, e)
raise ControllerError(message) raise ControllerError(message)
from . import Controller from . import Controller

View File

@ -18,21 +18,22 @@
import os import os
import html import html
import json import json
import copy
import uuid import uuid
import glob import glob
import shutil import shutil
import zipfile import zipfile
import jsonschema import pydantic
from typing import Optional
from ..version import __version__ from ..version import __version__
from ..schemas.topology import TOPOLOGY_SCHEMA
from ..schemas import dynamips_vm
from ..utils.qt import qt_font_to_style from ..utils.qt import qt_font_to_style
from ..compute.dynamips import PLATFORMS_DEFAULT_RAM from ..compute.dynamips import PLATFORMS_DEFAULT_RAM
from .controller_error import ControllerError from .controller_error import ControllerError
from gns3server.controller.schemas.topology import Topology
from gns3server.endpoints.schemas.dynamips_nodes import DynamipsCreate, NodeStatus
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -40,29 +41,21 @@ log = logging.getLogger(__name__)
GNS3_FILE_FORMAT_REVISION = 9 GNS3_FILE_FORMAT_REVISION = 9
class DynamipsNodeValidation(DynamipsCreate):
name: Optional[str] = None
def _check_topology_schema(topo): def _check_topology_schema(topo):
try: try:
jsonschema.validate(topo, TOPOLOGY_SCHEMA) Topology.parse_obj(topo)
# Check the nodes property against compute schemas # Check the nodes property against compute schemas
for node in topo["topology"].get("nodes", []): for node in topo["topology"].get("nodes", []):
schema = None
if node["node_type"] == "dynamips": if node["node_type"] == "dynamips":
schema = copy.deepcopy(dynamips_vm.VM_CREATE_SCHEMA) DynamipsNodeValidation.parse_obj(node.get("properties", {}))
if schema: except pydantic.ValidationError as e:
# Properties send to compute but in an other place in topology error = "Invalid data in topology file: {}".format(e)
delete_properties = ["name", "node_id"]
for prop in delete_properties:
del schema["properties"][prop]
schema["required"] = [p for p in schema["required"] if p not in delete_properties]
jsonschema.validate(node.get("properties", {}), schema)
except jsonschema.ValidationError as e:
error = "Invalid data in topology file: {} in schema: {}".format(
e.message,
json.dumps(e.schema))
log.critical(error) log.critical(error)
raise ControllerError(error) raise ControllerError(error)

View File

@ -27,13 +27,14 @@ log = logging.getLogger(__name__)
from fastapi import APIRouter, Request, Response, HTTPException, status from fastapi import APIRouter, Request, Response, HTTPException, status
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from typing import Union, List from typing import List
from uuid import UUID from uuid import UUID
from gns3server.endpoints.schemas.common import ErrorMessage from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas from gns3server.endpoints import schemas
from gns3server.controller import Controller from gns3server.controller import Controller
router = APIRouter() router = APIRouter()
responses = { responses = {
@ -41,11 +42,6 @@ responses = {
} }
#template_create_models = Union[schemas.VPCSTemplateCreate, schemas.CloudTemplateCreate, schemas.IOUTemplateCreate]
#template_update_models = Union[schemas.VPCSTemplateUpdate, schemas.CloudTemplateUpdate, schemas.IOUTemplateUpdate]
#template_response_models = Union[schemas.VPCSTemplate, schemas.CloudTemplate, schemas.IOUTemplate]
@router.post("/templates", @router.post("/templates",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Template) response_model=schemas.Template)

View File

@ -40,6 +40,3 @@ from .qemu_nodes import QemuCreate, QemuUpdate, Qemu, QemuDiskResize, QemuImageC
from .virtualbox_nodes import VirtualBoxCreate, VirtualBoxUpdate, VirtualBox from .virtualbox_nodes import VirtualBoxCreate, VirtualBoxUpdate, VirtualBox
from .vmware_nodes import VMwareCreate, VMwareUpdate, VMware from .vmware_nodes import VMwareCreate, VMwareUpdate, VMware
from .vpcs_nodes import VPCSCreate, VPCSUpdate, VPCS from .vpcs_nodes import VPCSCreate, VPCSUpdate, VPCS
from .vpcs_templates import VPCSTemplateCreate, VPCSTemplateUpdate, VPCSTemplate
from .cloud_templates import CloudTemplateCreate, CloudTemplateUpdate, CloudTemplate
from .iou_templates import IOUTemplateCreate, IOUTemplateUpdate, IOUTemplate

View File

@ -103,12 +103,12 @@ class Compute(ComputeBase):
compute_id: Union[str, UUID] compute_id: Union[str, UUID]
name: str name: str
connected: bool = Field(..., description="Whether the controller is connected to the compute or not") connected: Optional[bool] = Field(None, description="Whether the controller is connected to the compute or not")
cpu_usage_percent: float = Field(..., description="CPU usage of the compute", ge=0, le=100) cpu_usage_percent: Optional[float] = Field(None, description="CPU usage of the compute", ge=0, le=100)
memory_usage_percent: float = Field(..., description="Memory usage of the compute", ge=0, le=100) memory_usage_percent: Optional[float] = Field(None, description="Memory usage of the compute", ge=0, le=100)
disk_usage_percent: float = Field(..., description="Disk usage of the compute", ge=0, le=100) disk_usage_percent: Optional[float] = Field(None, description="Disk usage of the compute", ge=0, le=100)
last_error: Optional[str] = Field(None, description="Last error found on the compute") last_error: Optional[str] = Field(None, description="Last error found on the compute")
capabilities: Capabilities capabilities: Optional[Capabilities] = None
class AutoIdlePC(BaseModel): class AutoIdlePC(BaseModel):

View File

@ -25,6 +25,20 @@ from uuid import UUID
from .nodes import NodeStatus from .nodes import NodeStatus
class DynamipsPlatform(str, Enum):
"""
Supported Dynamips Platforms.
"""
c7200 = "c7200"
c3725 = "c3725"
c3745 = "c3745"
c3600 = "c3600"
c2691 = "c2691"
c2600 = "c2600"
c1700 = "c1700"
class DynamipsAdapters(str, Enum): class DynamipsAdapters(str, Enum):
""" """
Supported Dynamips Network Modules. Supported Dynamips Network Modules.
@ -56,6 +70,7 @@ class DynamipsAdapters(str, Enum):
gt96100_fe = "GT96100-FE" gt96100_fe = "GT96100-FE"
leopard_2fe = "Leopard-2FE" leopard_2fe = "Leopard-2FE"
class DynamipsWics(str, Enum): class DynamipsWics(str, Enum):
""" """
Supported Dynamips WICs. Supported Dynamips WICs.
@ -108,7 +123,7 @@ class DynamipsBase(BaseModel):
node_id: Optional[UUID] = None node_id: Optional[UUID] = None
name: Optional[str] = None name: Optional[str] = None
dynamips_id: Optional[int] = Field(None, description="Dynamips internal ID") dynamips_id: Optional[int] = Field(None, description="Dynamips internal ID")
platform: Optional[str] = Field(None, description="Cisco router platform", regex="^c[0-9]{4}$") platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
ram: Optional[int] = Field(None, description="Amount of RAM in MB") ram: Optional[int] = Field(None, description="Amount of RAM in MB")
nvram: Optional[int] = Field(None, description="Amount of NVRAM in KB") nvram: Optional[int] = Field(None, description="Amount of NVRAM in KB")
image: Optional[Path] = Field(None, description="Path to the IOS image") image: Optional[Path] = Field(None, description="Path to the IOS image")
@ -133,16 +148,16 @@ class DynamipsBase(BaseModel):
aux_type: Optional[DynamipsConsoleType] = Field(None, description="Auxiliary console type") aux_type: Optional[DynamipsConsoleType] = Field(None, description="Auxiliary console type")
mac_addr: Optional[str] = Field(None, description="Base MAC address", regex="^([0-9a-fA-F]{4}\\.){2}[0-9a-fA-F]{4}$") mac_addr: Optional[str] = Field(None, description="Base MAC address", regex="^([0-9a-fA-F]{4}\\.){2}[0-9a-fA-F]{4}$")
system_id: Optional[str] = Field(None, description="System ID") system_id: Optional[str] = Field(None, description="System ID")
slot0: Optional[str] = Field(None, description="Network module slot 0") slot0: Optional[DynamipsAdapters] = Field(None, description="Network module slot 0")
slot1: Optional[str] = Field(None, description="Network module slot 1") slot1: Optional[DynamipsAdapters] = Field(None, description="Network module slot 1")
slot2: Optional[str] = Field(None, description="Network module slot 2") slot2: Optional[DynamipsAdapters] = Field(None, description="Network module slot 2")
slot3: Optional[str] = Field(None, description="Network module slot 3") slot3: Optional[DynamipsAdapters] = Field(None, description="Network module slot 3")
slot4: Optional[str] = Field(None, description="Network module slot 4") slot4: Optional[DynamipsAdapters] = Field(None, description="Network module slot 4")
slot5: Optional[str] = Field(None, description="Network module slot 5") slot5: Optional[DynamipsAdapters] = Field(None, description="Network module slot 5")
slot6: Optional[str] = Field(None, description="Network module slot 6") slot6: Optional[DynamipsAdapters] = Field(None, description="Network module slot 6")
wic0: Optional[str] = Field(None, description="Network module WIC slot 0") wic0: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 0")
wic1: Optional[str] = Field(None, description="Network module WIC slot 1") wic1: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 1")
wic2: Optional[str] = Field(None, description="Network module WIC slot 2") wic2: Optional[DynamipsWics] = Field(None, description="Network module WIC slot 2")
npe: Optional[DynamipsNPE] = Field(None, description="NPE model") npe: Optional[DynamipsNPE] = Field(None, description="NPE model")
midplane: Optional[DynamipsMidplane] = Field(None, description="Midplane model") midplane: Optional[DynamipsMidplane] = Field(None, description="Midplane model")
sensors: Optional[List] = Field(None, description="Temperature sensors") sensors: Optional[List] = Field(None, description="Temperature sensors")

View File

@ -35,7 +35,7 @@ class ProjectStatus(str, Enum):
class Supplier(BaseModel): class Supplier(BaseModel):
logo: str = Field(..., description="Path to the project supplier logo") logo: str = Field(..., description="Path to the project supplier logo")
url: HttpUrl = Field(..., description="URL to the project supplier site") url: Optional[HttpUrl] = Field(None, description="URL to the project supplier site")
class Variable(BaseModel): class Variable(BaseModel):

View File

@ -104,6 +104,54 @@ class QemuProcessPriority(str, Enum):
very_low = "very low" very_low = "very low"
class QemuAdapterType(str, Enum):
"""
Supported Qemu VM adapter types.
"""
e1000 = "e1000"
e1000_82544gc = "e1000-82544gc"
e1000_82545em = "e1000-82545em"
e1000e = "e1000e"
i82550 = "i82550"
i82551 = "i82551"
i82557a = "i82557a"
i82557b = "i82557b"
i82557c = "i82557c"
i82558a = "i82558a"
i82558b = "i82558b"
i82559a = "i82559a"
i82559b = "i82559b"
i82559c = "i82559c"
i82559er = "i82559er"
i82562 = "i82562"
i82801 = "i82801"
ne2k_pci = "ne2k_pci"
pcnet = "pcnet"
rocker = "rocker"
rtl8139 = "rtl8139"
virtio = "virtio"
virtio_net_pci = "virtio-net-pci"
vmxnet3 = "vmxnet3"
class QemuDiskInterfaceType(str, Enum):
"""
Supported Qemu VM disk interface types.
"""
ide = "ide"
sate = "sata"
nvme = "nvme"
scsi = "scsi"
sd = "sd"
mtd = "mtd"
floppy = "floppy"
pflash = "pflash"
virtio = "virtio"
none = "none"
class QemuBase(BaseModel): class QemuBase(BaseModel):
""" """
Common Qemu node properties. Common Qemu node properties.
@ -121,16 +169,16 @@ class QemuBase(BaseModel):
aux_type: Optional[QemuConsoleType] = Field(None, description="Auxiliary console type") aux_type: Optional[QemuConsoleType] = Field(None, description="Auxiliary console type")
hda_disk_image: Optional[Path] = Field(None, description="QEMU hda disk image path") hda_disk_image: Optional[Path] = Field(None, description="QEMU hda disk image path")
hda_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hda disk image checksum") hda_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hda disk image checksum")
hda_disk_image_interface: Optional[str] = Field(None, description="QEMU hda interface") hda_disk_interface: Optional[QemuDiskInterfaceType] = Field(None, description="QEMU hda interface")
hdb_disk_image: Optional[Path] = Field(None, description="QEMU hdb disk image path") hdb_disk_image: Optional[Path] = Field(None, description="QEMU hdb disk image path")
hdb_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdb disk image checksum") hdb_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdb disk image checksum")
hdb_disk_image_interface: Optional[str] = Field(None, description="QEMU hdb interface") hdb_disk_interface: Optional[QemuDiskInterfaceType] = Field(None, description="QEMU hdb interface")
hdc_disk_image: Optional[Path] = Field(None, description="QEMU hdc disk image path") hdc_disk_image: Optional[Path] = Field(None, description="QEMU hdc disk image path")
hdc_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdc disk image checksum") hdc_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdc disk image checksum")
hdc_disk_image_interface: Optional[str] = Field(None, description="QEMU hdc interface") hdc_disk_interface: Optional[QemuDiskInterfaceType] = Field(None, description="QEMU hdc interface")
hdd_disk_image: Optional[Path] = Field(None, description="QEMU hdd disk image path") hdd_disk_image: Optional[Path] = Field(None, description="QEMU hdd disk image path")
hdd_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdd disk image checksum") hdd_disk_image_md5sum: Optional[str] = Field(None, description="QEMU hdd disk image checksum")
hdd_disk_image_interface: Optional[str] = Field(None, description="QEMU hdd interface") hdd_disk_interface: Optional[QemuDiskInterfaceType] = Field(None, description="QEMU hdd interface")
cdrom_image: Optional[Path] = Field(None, description="QEMU cdrom image path") cdrom_image: Optional[Path] = Field(None, description="QEMU cdrom image path")
cdrom_image_md5sum: Optional[str] = Field(None, description="QEMU cdrom image checksum") cdrom_image_md5sum: Optional[str] = Field(None, description="QEMU cdrom image checksum")
bios_image: Optional[Path] = Field(None, description="QEMU bios image path") bios_image: Optional[Path] = Field(None, description="QEMU bios image path")
@ -140,12 +188,12 @@ class QemuBase(BaseModel):
kernel_image: Optional[Path] = Field(None, description="QEMU kernel image path") kernel_image: Optional[Path] = Field(None, description="QEMU kernel image path")
kernel_image_md5sum: Optional[str] = Field(None, description="QEMU kernel image checksum") kernel_image_md5sum: Optional[str] = Field(None, description="QEMU kernel image checksum")
kernel_command_line: Optional[str] = Field(None, description="QEMU kernel command line") kernel_command_line: Optional[str] = Field(None, description="QEMU kernel command line")
boot_priotiry: Optional[QemuBootPriority] = Field(None, description="QEMU boot priority") boot_priority: Optional[QemuBootPriority] = Field(None, description="QEMU boot priority")
ram: Optional[int] = Field(None, description="Amount of RAM in MB") ram: Optional[int] = Field(None, description="Amount of RAM in MB")
cpus: Optional[int] = Field(None, ge=1, le=255, description="Number of vCPUs") cpus: Optional[int] = Field(None, ge=1, le=255, description="Number of vCPUs")
maxcpus: Optional[int] = Field(None, ge=1, le=255, description="Maximum number of hotpluggable vCPUs") maxcpus: Optional[int] = Field(None, ge=1, le=255, description="Maximum number of hotpluggable vCPUs")
adapters: Optional[int] = Field(None, ge=0, le=275, description="Number of adapters") adapters: Optional[int] = Field(None, ge=0, le=275, description="Number of adapters")
adapter_type: Optional[str] = Field(None, description="QEMU adapter type") adapter_type: Optional[QemuAdapterType] = Field(None, description="QEMU adapter type")
mac_address: Optional[str] = Field(None, description="QEMU MAC address", regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$") mac_address: Optional[str] = Field(None, description="QEMU MAC address", regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$")
legacy_networking: Optional[bool] = Field(None, description="Use QEMU legagy networking commands (-net syntax)") legacy_networking: Optional[bool] = Field(None, description="Use QEMU legagy networking commands (-net syntax)")
replicate_network_connection_state: Optional[bool] = Field(None, description="Replicate the network connection state for links in Qemu") replicate_network_connection_state: Optional[bool] = Field(None, description="Replicate the network connection state for links in Qemu")

View File

@ -45,8 +45,8 @@ class TemplateBase(BaseModel):
symbol: Optional[str] = None symbol: Optional[str] = None
builtin: Optional[bool] = None builtin: Optional[bool] = None
template_type: Optional[NodeType] = None template_type: Optional[NodeType] = None
usage: Optional[str] = None
compute_id: Optional[str] = None compute_id: Optional[str] = None
usage: Optional[str] = ""
class Config: class Config:
extra = "allow" extra = "allow"

View File

@ -42,6 +42,16 @@ class VirtualBoxOnCloseAction(str, Enum):
save_vm_state = "save_vm_state" save_vm_state = "save_vm_state"
class VirtualBoxAdapterType(str, Enum):
pcnet_pci_ii = "PCnet-PCI II (Am79C970A)",
pcnet_fast_iii = "PCNet-FAST III (Am79C973)",
intel_pro_1000_mt_desktop = "Intel PRO/1000 MT Desktop (82540EM)",
intel_pro_1000_t_server = "Intel PRO/1000 T Server (82543GC)",
intel_pro_1000_mt_server = "Intel PRO/1000 MT Server (82545EM)",
paravirtualized_network = "Paravirtualized Network (virtio-net)"
class VirtualBoxBase(BaseModel): class VirtualBoxBase(BaseModel):
""" """
Common VirtualBox node properties. Common VirtualBox node properties.
@ -54,14 +64,14 @@ class VirtualBoxBase(BaseModel):
usage: Optional[str] = Field(None, description="How to use the node") usage: Optional[str] = Field(None, description="How to use the node")
# 36 adapters is the maximum given by the ICH9 chipset in VirtualBox # 36 adapters is the maximum given by the ICH9 chipset in VirtualBox
adapters: Optional[int] = Field(None, ge=0, le=36, description="Number of adapters") adapters: Optional[int] = Field(None, ge=0, le=36, description="Number of adapters")
adapter_type: Optional[str] = Field(None, description="VirtualBox adapter type") adapter_type: Optional[VirtualBoxAdapterType] = Field(None, description="VirtualBox adapter type")
use_any_adapter: Optional[bool] = Field(None, description="Allow GNS3 to use any VirtualBox adapter") use_any_adapter: Optional[bool] = Field(None, description="Allow GNS3 to use any VirtualBox adapter")
console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port") console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port")
console_type: Optional[VirtualBoxConsoleType] = Field(None, description="Console type") console_type: Optional[VirtualBoxConsoleType] = Field(None, description="Console type")
ram: Optional[int] = Field(None, ge=0, le=65535, description="Amount of RAM in MB") ram: Optional[int] = Field(None, ge=0, le=65535, description="Amount of RAM in MB")
headless: Optional[bool] = Field(None, description="Headless mode") headless: Optional[bool] = Field(None, description="Headless mode")
on_close: Optional[VirtualBoxOnCloseAction] = Field(None, description="Action to execute on the VM is closed") on_close: Optional[VirtualBoxOnCloseAction] = Field(None, description="Action to execute on the VM is closed")
custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adpaters") custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adapters")
class VirtualBoxCreate(VirtualBoxBase): class VirtualBoxCreate(VirtualBoxBase):

View File

@ -43,6 +43,21 @@ class VMwareOnCloseAction(str, Enum):
save_vm_state = "save_vm_state" save_vm_state = "save_vm_state"
class VMwareAdapterType(str, Enum):
"""
Supported VMware VM adapter types.
"""
default = "default"
e1000 = "e1000"
e1000e = "e1000e"
flexible = "flexible"
vlance = "vlance"
vmxnet = "vmxnet"
vmxnet2 = "vmxnet2"
vmxnet3 = "vmxnet3"
class VMwareBase(BaseModel): class VMwareBase(BaseModel):
""" """
Common VMware node properties. Common VMware node properties.
@ -59,7 +74,7 @@ class VMwareBase(BaseModel):
on_close: Optional[VMwareOnCloseAction] = Field(None, description="Action to execute on the VM is closed") on_close: Optional[VMwareOnCloseAction] = Field(None, description="Action to execute on the VM is closed")
# 10 adapters is the maximum supported by VMware VMs. # 10 adapters is the maximum supported by VMware VMs.
adapters: Optional[int] = Field(None, ge=0, le=10, description="Number of adapters") adapters: Optional[int] = Field(None, ge=0, le=10, description="Number of adapters")
adapter_type: Optional[str] = Field(None, description="VMware adapter type") adapter_type: Optional[VMwareAdapterType] = Field(None, description="VMware adapter type")
use_any_adapter: Optional[bool] = Field(None, description="Allow GNS3 to use any VMware adapter") use_any_adapter: Optional[bool] = Field(None, description="Allow GNS3 to use any VMware adapter")
custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adpaters") custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adpaters")

View File

@ -23,7 +23,7 @@ from uuid import UUID
from .nodes import NodeStatus, CustomAdapter from .nodes import NodeStatus, CustomAdapter
class VPCSConsoleType(str, Enum): class ConsoleType(str, Enum):
""" """
Supported console types. Supported console types.
""" """
@ -41,7 +41,7 @@ class VPCSBase(BaseModel):
node_id: Optional[UUID] node_id: Optional[UUID]
usage: Optional[str] = Field(None, description="How to use the node") usage: Optional[str] = Field(None, description="How to use the node")
console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port") console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port")
console_type: Optional[VPCSConsoleType] = Field(None, description="Console type") console_type: Optional[ConsoleType] = Field(None, description="Console type")
startup_script: Optional[str] = Field(None, description="Content of the VPCS startup script") startup_script: Optional[str] = Field(None, description="Content of the VPCS startup script")

View File

@ -1,80 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import copy
from .template import BASE_TEMPLATE_PROPERTIES
ETHERNET_HUB_TEMPLATE_PROPERTIES = {
"ports_mapping": {
"type": "array",
"default": [{"port_number": 0,
"name": "Ethernet0"
},
{"port_number": 1,
"name": "Ethernet1"
},
{"port_number": 2,
"name": "Ethernet2"
},
{"port_number": 3,
"name": "Ethernet3"
},
{"port_number": 4,
"name": "Ethernet4"
},
{"port_number": 5,
"name": "Ethernet5"
},
{"port_number": 6,
"name": "Ethernet6"
},
{"port_number": 7,
"name": "Ethernet7"
}
],
"items": [
{"type": "object",
"oneOf": [{"description": "Ethernet port",
"properties": {"name": {"description": "Port name",
"type": "string",
"minLength": 1},
"port_number": {
"description": "Port number",
"type": "integer",
"minimum": 0}
},
"required": ["name", "port_number"],
"additionalProperties": False}
],
}
]
}
}
ETHERNET_HUB_TEMPLATE_PROPERTIES.update(copy.deepcopy(BASE_TEMPLATE_PROPERTIES))
ETHERNET_HUB_TEMPLATE_PROPERTIES["category"]["default"] = "switch"
ETHERNET_HUB_TEMPLATE_PROPERTIES["default_name_format"]["default"] = "Hub{0}"
ETHERNET_HUB_TEMPLATE_PROPERTIES["symbol"]["default"] = ":/symbols/hub.svg"
ETHERNET_HUB_TEMPLATE_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "An Ethernet hub template object",
"type": "object",
"properties": ETHERNET_HUB_TEMPLATE_PROPERTIES,
"additionalProperties": False
}

View File

@ -655,7 +655,8 @@ async def test_clean_pictures_and_keep_supplier_logo(project):
""" """
project.supplier = { project.supplier = {
'logo': 'logo.png' 'logo': 'logo.png',
'url': 'http://acme.com'
} }
drawing = await project.add_drawing() drawing = await project.add_drawing()

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import jsonschema import pydantic
from gns3server.controller.template import Template from gns3server.controller.template import Template
@ -39,7 +39,7 @@ def test_template_json():
def test_template_json_with_not_known_category(): def test_template_json_with_not_known_category():
with pytest.raises(jsonschema.ValidationError): with pytest.raises(pydantic.ValidationError):
Template(None, { Template(None, {
"node_type": "qemu", "node_type": "qemu",
"name": "Test", "name": "Test",

View File

@ -908,7 +908,12 @@ async def test_cloud_template_create(controller_api):
"compute_id": "local", "compute_id": "local",
"default_name_format": "Cloud{0}", "default_name_format": "Cloud{0}",
"name": "Cloud template", "name": "Cloud template",
"symbol": ":/symbols/cloud.svg"} "ports_mapping": [],
"symbol": ":/symbols/cloud.svg",
"remote_console_host": "127.0.0.1",
"remote_console_port": 23,
"remote_console_type": "none",
"remote_console_http_path": "/"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json.get(item) == value