Use pydantic for data validation (instead of jsonschema)

Fix/improve various pydantic shema models.
pull/1906/head
grossmj 4 years ago
parent d6a65883ee
commit 81439c750a

@ -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
)

@ -16,30 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .templates import Category, TemplateBase
from .cloud_nodes import EthernetPort, TAPPort, UDPPort
from gns3server.endpoints.schemas.templates import Category, TemplateBase
from gns3server.endpoints.schemas.cloud_nodes import EthernetPort, TAPPort, UDPPort, CloudConsoleType
from pydantic import Field
from typing import Optional, Union, List
from enum import Enum
from .nodes import NodeType
class RemoteConsoleType(str, Enum):
"""
Supported remote console types for cloud nodes.
"""
none = "none"
telnet = "telnet"
vnc = "vnc"
spice = "spice"
http = "http"
https = "https"
class CloudTemplateBase(TemplateBase):
class CloudTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "Cloud{0}"
@ -47,28 +31,5 @@ class CloudTemplateBase(TemplateBase):
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_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_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]
remote_console_type: Optional[CloudConsoleType] = Field("none", description="Remote console type")
remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface")

@ -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")

@ -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")

@ -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")

@ -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")

@ -16,31 +16,19 @@
# 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 pathlib import Path
from typing import Optional, Union
from enum import Enum
from typing import Optional
from .nodes import NodeType
class ConsoleType(str, Enum):
"""
Supported console types for IOU nodes
"""
none = "none"
telnet = "telnet"
class IOUTemplateBase(TemplateBase):
class IOUTemplate(TemplateBase):
category: Optional[Category] = "router"
default_name_format: Optional[str] = "IOU{0}"
symbol: Optional[str] = ":/symbols/multilayer_switch.svg"
path: Path = Field(..., description="Path of IOU executable")
ethernet_adapters: Optional[int] = Field(2, description="Number of ethernet 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_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]

@ -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")

@ -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()

@ -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")

@ -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")

@ -16,53 +16,18 @@
# 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 typing import Optional, Union
from enum import Enum
from typing import Optional
from .nodes import NodeType
class ConsoleType(str, Enum):
"""
Supported console types for VPCS nodes
"""
none = "none"
telnet = "telnet"
class VPCSTemplateBase(TemplateBase):
class VPCSTemplate(TemplateBase):
category: Optional[Category] = "guest"
default_name_format: Optional[str] = "PC{0}"
symbol: Optional[str] = ":/symbols/vpcs_guest.svg"
base_script_file: Optional[str] = Field("vpcs_base_config.txt", description="Script file")
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")
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]

@ -1,6 +1,6 @@
#!/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
# it under the terms of the GNU General Public License as published by
@ -17,57 +17,14 @@
import copy
import uuid
import json
import jsonschema
from gns3server.schemas.cloud_template import CLOUD_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.ethernet_switch_template import ETHERNET_SWITCH_TEMPLATE_OBJECT_SCHEMA
from gns3server.schemas.ethernet_hub_template import ETHERNET_HUB_TEMPLATE_OBJECT_SCHEMA
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
)
from pydantic import ValidationError
from fastapi.encoders import jsonable_encoder
from gns3server.controller import schemas
import logging
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 = {
3: "firewall",
2: "guest",
@ -76,27 +33,26 @@ ID_TO_CATEGORY = {
}
TEMPLATE_TYPE_TO_SHEMA = {
"cloud": CLOUD_TEMPLATE_OBJECT_SCHEMA,
"ethernet_hub": ETHERNET_HUB_TEMPLATE_OBJECT_SCHEMA,
"ethernet_switch": ETHERNET_SWITCH_TEMPLATE_OBJECT_SCHEMA,
"docker": DOCKER_TEMPLATE_OBJECT_SCHEMA,
"dynamips": DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"vpcs": VPCS_TEMPLATE_OBJECT_SCHEMA,
"traceng": TRACENG_TEMPLATE_OBJECT_SCHEMA,
"virtualbox": VIRTUALBOX_TEMPLATE_OBJECT_SCHEMA,
"vmware": VMWARE_TEMPLATE_OBJECT_SCHEMA,
"iou": IOU_TEMPLATE_OBJECT_SCHEMA,
"qemu": QEMU_TEMPLATE_OBJECT_SCHEMA
"cloud": schemas.CloudTemplate,
"ethernet_hub": schemas.EthernetHubTemplate,
"ethernet_switch": schemas.EthernetSwitchTemplate,
"docker": schemas.DockerTemplate,
"dynamips": schemas.DynamipsTemplate,
"vpcs": schemas.VPCSTemplate,
"virtualbox": schemas.VirtualBoxTemplate,
"vmware": schemas.VMwareTemplate,
"iou": schemas.IOUTemplate,
"qemu": schemas.QemuTemplate
}
DYNAMIPS_PLATFORM_TO_SHEMA = {
"c7200": C7200_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c3745": C3745_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c3725": C3725_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c3600": C3600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c2691": C2691_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c2600": C2600_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA,
"c1700": C1700_DYNAMIPS_TEMPLATE_OBJECT_SCHEMA
"c7200": schemas.C7200DynamipsTemplate,
"c3745": schemas.C3745DynamipsTemplate,
"c3725": schemas.C3725DynamipsTemplate,
"c3600": schemas.C3600DynamipsTemplate,
"c2691": schemas.C2691DynamipsTemplate,
"c2600": schemas.C2600DynamipsTemplate,
"c1700": schemas.C1700DynamipsTemplate
}
@ -141,11 +97,18 @@ class Template:
self._builtin = builtin
if builtin is False:
self.validate_and_apply_defaults(TEMPLATE_TYPE_TO_SHEMA[self.template_type])
if self.template_type == "dynamips":
# 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"]])
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":
# special case for Dynamips to cover all platform types that contain specific settings
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))
@ -179,7 +142,6 @@ class Template:
def update(self, **kwargs):
from gns3server.controller import Controller
controller = Controller.instance()
Controller.instance().check_can_write_config()
@ -187,17 +149,6 @@ class Template:
controller.notification.controller_emit("template.updated", self.__json__())
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):
"""
Template settings.

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

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

@ -27,13 +27,14 @@ log = logging.getLogger(__name__)
from fastapi import APIRouter, Request, Response, HTTPException, status
from fastapi.encoders import jsonable_encoder
from typing import Union, List
from typing import List
from uuid import UUID
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas
from gns3server.controller import Controller
router = APIRouter()
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",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Template)

@ -40,6 +40,3 @@ from .qemu_nodes import QemuCreate, QemuUpdate, Qemu, QemuDiskResize, QemuImageC
from .virtualbox_nodes import VirtualBoxCreate, VirtualBoxUpdate, VirtualBox
from .vmware_nodes import VMwareCreate, VMwareUpdate, VMware
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

@ -103,12 +103,12 @@ class Compute(ComputeBase):
compute_id: Union[str, UUID]
name: str
connected: bool = Field(..., 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)
memory_usage_percent: float = Field(..., 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)
connected: Optional[bool] = Field(None, description="Whether the controller is connected to the compute or not")
cpu_usage_percent: Optional[float] = Field(None, description="CPU 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: 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")
capabilities: Capabilities
capabilities: Optional[Capabilities] = None
class AutoIdlePC(BaseModel):

@ -25,6 +25,20 @@ from uuid import UUID
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):
"""
Supported Dynamips Network Modules.
@ -56,6 +70,7 @@ class DynamipsAdapters(str, Enum):
gt96100_fe = "GT96100-FE"
leopard_2fe = "Leopard-2FE"
class DynamipsWics(str, Enum):
"""
Supported Dynamips WICs.
@ -108,7 +123,7 @@ class DynamipsBase(BaseModel):
node_id: Optional[UUID] = None
name: Optional[str] = None
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")
nvram: Optional[int] = Field(None, description="Amount of NVRAM in KB")
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")
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")
slot0: Optional[str] = Field(None, description="Network module slot 0")
slot1: Optional[str] = Field(None, description="Network module slot 1")
slot2: Optional[str] = Field(None, description="Network module slot 2")
slot3: Optional[str] = Field(None, description="Network module slot 3")
slot4: Optional[str] = Field(None, description="Network module slot 4")
slot5: Optional[str] = Field(None, description="Network module slot 5")
slot6: Optional[str] = Field(None, description="Network module slot 6")
wic0: Optional[str] = Field(None, description="Network module WIC slot 0")
wic1: Optional[str] = Field(None, description="Network module WIC slot 1")
wic2: Optional[str] = Field(None, description="Network module WIC slot 2")
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")
npe: Optional[DynamipsNPE] = Field(None, description="NPE model")
midplane: Optional[DynamipsMidplane] = Field(None, description="Midplane model")
sensors: Optional[List] = Field(None, description="Temperature sensors")

@ -35,7 +35,7 @@ class ProjectStatus(str, Enum):
class Supplier(BaseModel):
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):

@ -104,6 +104,54 @@ class QemuProcessPriority(str, Enum):
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):
"""
Common Qemu node properties.
@ -121,16 +169,16 @@ class QemuBase(BaseModel):
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_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_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_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_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_md5sum: Optional[str] = Field(None, description="QEMU cdrom image checksum")
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_md5sum: Optional[str] = Field(None, description="QEMU kernel image checksum")
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")
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")
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})$")
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")

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

@ -42,6 +42,16 @@ class VirtualBoxOnCloseAction(str, Enum):
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):
"""
Common VirtualBox node properties.
@ -54,14 +64,14 @@ class VirtualBoxBase(BaseModel):
usage: Optional[str] = Field(None, description="How to use the node")
# 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")
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")
console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port")
console_type: Optional[VirtualBoxConsoleType] = Field(None, description="Console type")
ram: Optional[int] = Field(None, ge=0, le=65535, description="Amount of RAM in MB")
headless: Optional[bool] = Field(None, description="Headless mode")
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):

@ -43,6 +43,21 @@ class VMwareOnCloseAction(str, Enum):
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):
"""
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")
# 10 adapters is the maximum supported by VMware VMs.
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")
custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adpaters")

@ -23,7 +23,7 @@ from uuid import UUID
from .nodes import NodeStatus, CustomAdapter
class VPCSConsoleType(str, Enum):
class ConsoleType(str, Enum):
"""
Supported console types.
"""
@ -41,7 +41,7 @@ class VPCSBase(BaseModel):
node_id: Optional[UUID]
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_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")

@ -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
}

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

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

@ -908,7 +908,12 @@ async def test_cloud_template_create(controller_api):
"compute_id": "local",
"default_name_format": "Cloud{0}",
"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():
assert response.json.get(item) == value

Loading…
Cancel
Save