diff --git a/gns3server/api/routes/compute/__init__.py b/gns3server/api/routes/compute/__init__.py index b60dfb7c..57b7225d 100644 --- a/gns3server/api/routes/compute/__init__.py +++ b/gns3server/api/routes/compute/__init__.py @@ -138,35 +138,103 @@ async def http_exception_handler(request: Request, exc: StarletteHTTPException): ) -compute_api.include_router(capabilities.router, tags=["Capabilities"]) -compute_api.include_router(compute.router, tags=["Compute"]) -compute_api.include_router(notifications.router, tags=["Notifications"]) -compute_api.include_router(projects.router, tags=["Projects"]) -compute_api.include_router(images.router, tags=["Images"]) compute_api.include_router( - atm_switch_nodes.router, prefix="/projects/{project_id}/atm_switch/nodes", tags=["ATM switch"] + capabilities.router, + tags=["Capabilities"] ) -compute_api.include_router(cloud_nodes.router, prefix="/projects/{project_id}/cloud/nodes", tags=["Cloud nodes"]) -compute_api.include_router(docker_nodes.router, prefix="/projects/{project_id}/docker/nodes", tags=["Docker nodes"]) + compute_api.include_router( - dynamips_nodes.router, prefix="/projects/{project_id}/dynamips/nodes", tags=["Dynamips nodes"] + compute.router, + tags=["Compute"] +) + +compute_api.include_router( + notifications.router, + tags=["Notifications"] +) + +compute_api.include_router( + projects.router, + tags=["Projects"] +) + +compute_api.include_router( + images.router, + tags=["Images"] +) + +compute_api.include_router( + atm_switch_nodes.router, + prefix="/projects/{project_id}/atm_switch/nodes", + tags=["ATM switch"] ) compute_api.include_router( - ethernet_hub_nodes.router, prefix="/projects/{project_id}/ethernet_hub/nodes", tags=["Ethernet hub nodes"] + cloud_nodes.router, + prefix="/projects/{project_id}/cloud/nodes", + tags=["Cloud nodes"] ) + compute_api.include_router( - ethernet_switch_nodes.router, prefix="/projects/{project_id}/ethernet_switch/nodes", tags=["Ethernet switch nodes"] + docker_nodes.router, + prefix="/projects/{project_id}/docker/nodes", + tags=["Docker nodes"] ) + +compute_api.include_router( + dynamips_nodes.router, + prefix="/projects/{project_id}/dynamips/nodes", + tags=["Dynamips nodes"] +) + +compute_api.include_router( + ethernet_hub_nodes.router, + prefix="/projects/{project_id}/ethernet_hub/nodes", + tags=["Ethernet hub nodes"] +) + +compute_api.include_router( + ethernet_switch_nodes.router, + prefix="/projects/{project_id}/ethernet_switch/nodes", + tags=["Ethernet switch nodes"] +) + compute_api.include_router( frame_relay_switch_nodes.router, prefix="/projects/{project_id}/frame_relay_switch/nodes", - tags=["Frame Relay switch nodes"], + tags=["Frame Relay switch nodes"] ) -compute_api.include_router(iou_nodes.router, prefix="/projects/{project_id}/iou/nodes", tags=["IOU nodes"]) -compute_api.include_router(nat_nodes.router, prefix="/projects/{project_id}/nat/nodes", tags=["NAT nodes"]) -compute_api.include_router(qemu_nodes.router, prefix="/projects/{project_id}/qemu/nodes", tags=["Qemu nodes"]) + compute_api.include_router( - virtualbox_nodes.router, prefix="/projects/{project_id}/virtualbox/nodes", tags=["VirtualBox nodes"] + iou_nodes.router, + prefix="/projects/{project_id}/iou/nodes", + tags=["IOU nodes"]) + +compute_api.include_router( + nat_nodes.router, + prefix="/projects/{project_id}/nat/nodes", + tags=["NAT nodes"] +) + +compute_api.include_router( + qemu_nodes.router, + prefix="/projects/{project_id}/qemu/nodes", + tags=["Qemu nodes"] +) + +compute_api.include_router( + virtualbox_nodes.router, + prefix="/projects/{project_id}/virtualbox/nodes", + tags=["VirtualBox nodes"] +) + +compute_api.include_router( + vmware_nodes.router, + prefix="/projects/{project_id}/vmware/nodes", + tags=["VMware nodes"] +) + +compute_api.include_router( + vpcs_nodes.router, + prefix="/projects/{project_id}/vpcs/nodes", + tags=["VPCS nodes"] ) -compute_api.include_router(vmware_nodes.router, prefix="/projects/{project_id}/vmware/nodes", tags=["VMware nodes"]) -compute_api.include_router(vpcs_nodes.router, prefix="/projects/{project_id}/vpcs/nodes", tags=["VPCS nodes"]) diff --git a/gns3server/api/routes/compute/virtualbox_nodes.py b/gns3server/api/routes/compute/virtualbox_nodes.py index f558a02e..7fa51d9e 100644 --- a/gns3server/api/routes/compute/virtualbox_nodes.py +++ b/gns3server/api/routes/compute/virtualbox_nodes.py @@ -149,11 +149,6 @@ async def start_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)): Start a VirtualBox node. """ - if await node.check_hw_virtualization(): - pm = ProjectManager.instance() - if pm.check_hardware_virtualization(node) is False: - pass # FIXME: check this - # raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox") await node.start() diff --git a/gns3server/api/routes/compute/vmware_nodes.py b/gns3server/api/routes/compute/vmware_nodes.py index 1d857f1e..2617f791 100644 --- a/gns3server/api/routes/compute/vmware_nodes.py +++ b/gns3server/api/routes/compute/vmware_nodes.py @@ -117,11 +117,6 @@ async def start_vmware_node(node: VMwareVM = Depends(dep_node)): Start a VMware node. """ - if node.check_hw_virtualization(): - pm = ProjectManager.instance() - if pm.check_hardware_virtualization(node) is False: - pass # FIXME: check this - # raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox") await node.start() diff --git a/gns3server/api/routes/controller/nodes.py b/gns3server/api/routes/controller/nodes.py index e14dd334..93e2a28b 100644 --- a/gns3server/api/routes/controller/nodes.py +++ b/gns3server/api/routes/controller/nodes.py @@ -108,7 +108,7 @@ async def dep_node(node_id: UUID, project: Project = Depends(dep_project)): 409: {"model": schemas.ErrorMessage, "description": "Could not create node"}, }, ) -async def create_node(node_data: schemas.Node, project: Project = Depends(dep_project)): +async def create_node(node_data: schemas.NodeCreate, project: Project = Depends(dep_project)): """ Create a new node. """ diff --git a/gns3server/compute/project_manager.py b/gns3server/compute/project_manager.py index b657007c..d1f77cb8 100644 --- a/gns3server/compute/project_manager.py +++ b/gns3server/compute/project_manager.py @@ -120,18 +120,3 @@ class ProjectManager: if project_id not in self._projects: raise ComputeNotFoundError(f"Project ID {project_id} doesn't exist") del self._projects[project_id] - - def check_hardware_virtualization(self, source_node): - """ - Checks if hardware virtualization can be used. - - :returns: boolean - """ - - for project in self._projects.values(): - for node in project.nodes: - if node == source_node: - continue - if node.hw_virtualization and node.__class__.__name__ != source_node.__class__.__name__: - return False - return True diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 7e618210..87f825bb 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -47,7 +47,7 @@ from ...utils.asyncio import monitor_process from ...utils.images import md5sum from ...utils import macaddress_to_int, int_to_macaddress -from gns3server.schemas.qemu_nodes import Qemu, QemuPlatform +from gns3server.schemas.compute.qemu_nodes import Qemu, QemuPlatform import logging diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 46b3c966..f7976b90 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -353,6 +353,7 @@ class Controller: self._computes[compute.id] = compute # self.save() if connect: + # call compute.connect() later to give time to the controller to be fully started asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(compute.connect())) self.notification.controller_emit("compute.created", compute.__json__()) return compute diff --git a/gns3server/controller/drawing.py b/gns3server/controller/drawing.py index a1a89809..83e174da 100644 --- a/gns3server/controller/drawing.py +++ b/gns3server/controller/drawing.py @@ -203,6 +203,7 @@ class Drawing: """ :param topology_dump: Filter to keep only properties require for saving on disk """ + if topology_dump: return { "drawing_id": self._id, diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index 687f2b29..fa2a54d6 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -31,8 +31,8 @@ from ..utils.qt import qt_font_to_style from ..compute.dynamips import PLATFORMS_DEFAULT_RAM from .controller_error import ControllerError -from gns3server.schemas.topology import Topology -from gns3server.schemas.dynamips_nodes import DynamipsCreate +from gns3server.schemas.controller.topology import Topology +from gns3server.schemas.compute.dynamips_nodes import DynamipsCreate import logging diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py index 4623c487..5080c7d9 100644 --- a/gns3server/core/tasks.py +++ b/gns3server/core/tasks.py @@ -33,6 +33,10 @@ log = logging.getLogger(__name__) def create_startup_handler(app: FastAPI) -> Callable: + """ + Tasks to be performed when the server is starting. + """ + async def start_app() -> None: loop = asyncio.get_event_loop() logger = logging.getLogger("asyncio") @@ -77,6 +81,10 @@ def create_startup_handler(app: FastAPI) -> Callable: def create_shutdown_handler(app: FastAPI) -> Callable: + """ + Tasks to be performed when the server is shutdown. + """ + async def shutdown_handler() -> None: await HTTPClient.close_session() await Controller.instance().stop() diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gns3server/schemas/__init__.py b/gns3server/schemas/__init__.py index 022d428e..8ff9d6f0 100644 --- a/gns3server/schemas/__init__.py +++ b/gns3server/schemas/__init__.py @@ -14,46 +14,36 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# General schemas from .config import ServerConfig -from .iou_license import IOULicense -from .links import Link from .common import ErrorMessage from .version import Version -from .computes import ComputeCreate, ComputeUpdate, AutoIdlePC, Compute -from .templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template -from .drawings import Drawing -from .gns3vm import GNS3VM -from .nodes import NodeUpdate, NodeDuplicate, NodeCapture, Node -from .projects import ProjectCreate, ProjectUpdate, ProjectDuplicate, Project, ProjectFile -from .users import UserCreate, UserUpdate, User -from .tokens import Token -from .snapshots import SnapshotCreate, Snapshot -from .capabilities import Capabilities -from .nios import UDPNIO, TAPNIO, EthernetNIO -from .atm_switch_nodes import ATMSwitchCreate, ATMSwitchUpdate, ATMSwitch -from .cloud_nodes import CloudCreate, CloudUpdate, Cloud -from .docker_nodes import DockerCreate, DockerUpdate, Docker -from .dynamips_nodes import DynamipsCreate, DynamipsUpdate, Dynamips -from .ethernet_hub_nodes import EthernetHubCreate, EthernetHubUpdate, EthernetHub -from .ethernet_switch_nodes import EthernetSwitchCreate, EthernetSwitchUpdate, EthernetSwitch -from .frame_relay_switch_nodes import FrameRelaySwitchCreate, FrameRelaySwitchUpdate, FrameRelaySwitch -from .qemu_nodes import QemuCreate, QemuUpdate, QemuImageCreate, QemuImageUpdate, QemuDiskResize, Qemu -from .iou_nodes import IOUCreate, IOUUpdate, IOUStart, IOU -from .nat_nodes import NATCreate, NATUpdate, NAT -from .vpcs_nodes import VPCSCreate, VPCSUpdate, VPCS -from .vmware_nodes import VMwareCreate, VMwareUpdate, VMware -from .virtualbox_nodes import VirtualBoxCreate, VirtualBoxUpdate, VirtualBox -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 ( +# Controller schemas +from .controller.links import Link +from .controller.computes import ComputeCreate, ComputeUpdate, AutoIdlePC, Compute +from .controller.templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template +from .controller.drawings import Drawing +from .controller.gns3vm import GNS3VM +from .controller.nodes import NodeCreate, NodeUpdate, NodeDuplicate, NodeCapture, Node +from .controller.projects import ProjectCreate, ProjectUpdate, ProjectDuplicate, Project, ProjectFile +from .controller.users import UserCreate, UserUpdate, User +from .controller.tokens import Token +from .controller.snapshots import SnapshotCreate, Snapshot +from .controller.iou_license import IOULicense +from .controller.capabilities import Capabilities + +# Controller template schemas +from .controller.templates.vpcs_templates import VPCSTemplate +from .controller.templates.cloud_templates import CloudTemplate +from .controller.templates.iou_templates import IOUTemplate +from .controller.templates.docker_templates import DockerTemplate +from .controller.templates.ethernet_hub_templates import EthernetHubTemplate +from .controller.templates.ethernet_switch_templates import EthernetSwitchTemplate +from .controller.templates.virtualbox_templates import VirtualBoxTemplate +from .controller.templates.vmware_templates import VMwareTemplate +from .controller.templates.qemu_templates import QemuTemplate +from .controller.templates.dynamips_templates import ( DynamipsTemplate, C1700DynamipsTemplate, C2600DynamipsTemplate, @@ -63,3 +53,19 @@ from .dynamips_templates import ( C3745DynamipsTemplate, C7200DynamipsTemplate, ) + +# Compute schemas +from .compute.nios import UDPNIO, TAPNIO, EthernetNIO +from .compute.atm_switch_nodes import ATMSwitchCreate, ATMSwitchUpdate, ATMSwitch +from .compute.cloud_nodes import CloudCreate, CloudUpdate, Cloud +from .compute.docker_nodes import DockerCreate, DockerUpdate, Docker +from .compute.dynamips_nodes import DynamipsCreate, DynamipsUpdate, Dynamips +from .compute.ethernet_hub_nodes import EthernetHubCreate, EthernetHubUpdate, EthernetHub +from .compute.ethernet_switch_nodes import EthernetSwitchCreate, EthernetSwitchUpdate, EthernetSwitch +from .compute.frame_relay_switch_nodes import FrameRelaySwitchCreate, FrameRelaySwitchUpdate, FrameRelaySwitch +from .compute.qemu_nodes import QemuCreate, QemuUpdate, QemuImageCreate, QemuImageUpdate, QemuDiskResize, Qemu +from .compute.iou_nodes import IOUCreate, IOUUpdate, IOUStart, IOU +from .compute.nat_nodes import NATCreate, NATUpdate, NAT +from .compute.vpcs_nodes import VPCSCreate, VPCSUpdate, VPCS +from .compute.vmware_nodes import VMwareCreate, VMwareUpdate, VMware +from .compute.virtualbox_nodes import VirtualBoxCreate, VirtualBoxUpdate, VirtualBox diff --git a/gns3server/schemas/common.py b/gns3server/schemas/common.py index 85ae01d3..e524384f 100644 --- a/gns3server/schemas/common.py +++ b/gns3server/schemas/common.py @@ -16,6 +16,7 @@ from pydantic import BaseModel, Field from typing import Optional, Union +from enum import Enum class ErrorMessage(BaseModel): @@ -26,14 +27,45 @@ class ErrorMessage(BaseModel): message: str -class Label(BaseModel): +class NodeStatus(str, Enum): """ - Label data. - + Supported node statuses. """ - text: str - style: Optional[Union[str, None]] = Field(None, description="SVG style attribute. Apply default style if null") - x: Optional[Union[int, None]] = Field(None, description="Relative X position of the label. Center it if null") - y: Optional[int] = Field(None, description="Relative Y position of the label") - rotation: Optional[int] = Field(None, ge=-359, le=360, description="Rotation of the label") + stopped = "stopped" + started = "started" + suspended = "suspended" + + +class CustomAdapter(BaseModel): + """ + Custom adapter data. + """ + + adapter_number: int + port_name: Optional[str] = None + adapter_type: Optional[str] = None + mac_address: Optional[str] = Field(None, regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$") + + +class ConsoleType(str, Enum): + """ + Supported console types. + """ + + vnc = "vnc" + telnet = "telnet" + http = "http" + https = "https" + spice = "spice" + spice_agent = "spice+agent" + none = "none" + + +class AuxType(str, Enum): + """ + Supported auxiliary console types. + """ + + telnet = "telnet" + none = "none" diff --git a/gns3server/handlers/api/compute/ethernet_switch_handler.py b/gns3server/schemas/compute/__init__.py similarity index 100% rename from gns3server/handlers/api/compute/ethernet_switch_handler.py rename to gns3server/schemas/compute/__init__.py diff --git a/gns3server/schemas/atm_switch_nodes.py b/gns3server/schemas/compute/atm_switch_nodes.py similarity index 97% rename from gns3server/schemas/atm_switch_nodes.py rename to gns3server/schemas/compute/atm_switch_nodes.py index 6f9e55b1..836fbfa3 100644 --- a/gns3server/schemas/atm_switch_nodes.py +++ b/gns3server/schemas/compute/atm_switch_nodes.py @@ -18,7 +18,7 @@ from pydantic import BaseModel from typing import Optional from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class ATMSwitchBase(BaseModel): diff --git a/gns3server/schemas/cloud_nodes.py b/gns3server/schemas/compute/cloud_nodes.py similarity index 99% rename from gns3server/schemas/cloud_nodes.py rename to gns3server/schemas/compute/cloud_nodes.py index 7ef0fe02..92d8aadd 100644 --- a/gns3server/schemas/cloud_nodes.py +++ b/gns3server/schemas/compute/cloud_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, Union, List from enum import Enum from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class HostInterfaceType(Enum): diff --git a/gns3server/schemas/docker_nodes.py b/gns3server/schemas/compute/docker_nodes.py similarity index 97% rename from gns3server/schemas/docker_nodes.py rename to gns3server/schemas/compute/docker_nodes.py index 4f033c85..7129aaae 100644 --- a/gns3server/schemas/docker_nodes.py +++ b/gns3server/schemas/compute/docker_nodes.py @@ -18,7 +18,7 @@ from pydantic import BaseModel, Field from typing import Optional, List from uuid import UUID -from .nodes import CustomAdapter, ConsoleType, AuxType, NodeStatus +from ..common import NodeStatus, CustomAdapter, ConsoleType, AuxType class DockerBase(BaseModel): diff --git a/gns3server/schemas/dynamips_nodes.py b/gns3server/schemas/compute/dynamips_nodes.py similarity index 99% rename from gns3server/schemas/dynamips_nodes.py rename to gns3server/schemas/compute/dynamips_nodes.py index 1289c013..64bc07c2 100644 --- a/gns3server/schemas/dynamips_nodes.py +++ b/gns3server/schemas/compute/dynamips_nodes.py @@ -20,7 +20,7 @@ from typing import Optional, List from enum import Enum from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class DynamipsPlatform(str, Enum): diff --git a/gns3server/schemas/ethernet_hub_nodes.py b/gns3server/schemas/compute/ethernet_hub_nodes.py similarity index 97% rename from gns3server/schemas/ethernet_hub_nodes.py rename to gns3server/schemas/compute/ethernet_hub_nodes.py index e559a6d4..803be8f3 100644 --- a/gns3server/schemas/ethernet_hub_nodes.py +++ b/gns3server/schemas/compute/ethernet_hub_nodes.py @@ -18,7 +18,7 @@ from pydantic import BaseModel from typing import Optional, List from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class EthernetHubPort(BaseModel): diff --git a/gns3server/schemas/ethernet_switch_nodes.py b/gns3server/schemas/compute/ethernet_switch_nodes.py similarity index 98% rename from gns3server/schemas/ethernet_switch_nodes.py rename to gns3server/schemas/compute/ethernet_switch_nodes.py index 9b1050ec..0edd7f1b 100644 --- a/gns3server/schemas/ethernet_switch_nodes.py +++ b/gns3server/schemas/compute/ethernet_switch_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, List from uuid import UUID from enum import Enum -from .nodes import NodeStatus +from ..common import NodeStatus class EthernetSwitchPortType(Enum): diff --git a/gns3server/schemas/frame_relay_switch_nodes.py b/gns3server/schemas/compute/frame_relay_switch_nodes.py similarity index 97% rename from gns3server/schemas/frame_relay_switch_nodes.py rename to gns3server/schemas/compute/frame_relay_switch_nodes.py index fb6e580c..6b759eb4 100644 --- a/gns3server/schemas/frame_relay_switch_nodes.py +++ b/gns3server/schemas/compute/frame_relay_switch_nodes.py @@ -18,7 +18,7 @@ from pydantic import BaseModel from typing import Optional from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class FrameRelaySwitchBase(BaseModel): diff --git a/gns3server/schemas/iou_nodes.py b/gns3server/schemas/compute/iou_nodes.py similarity index 98% rename from gns3server/schemas/iou_nodes.py rename to gns3server/schemas/compute/iou_nodes.py index f237fdd9..aa09323b 100644 --- a/gns3server/schemas/iou_nodes.py +++ b/gns3server/schemas/compute/iou_nodes.py @@ -18,7 +18,7 @@ from pydantic import BaseModel, Field from typing import Optional from uuid import UUID -from .nodes import ConsoleType, NodeStatus +from ..common import NodeStatus, ConsoleType class IOUBase(BaseModel): diff --git a/gns3server/schemas/nat_nodes.py b/gns3server/schemas/compute/nat_nodes.py similarity index 98% rename from gns3server/schemas/nat_nodes.py rename to gns3server/schemas/compute/nat_nodes.py index d2edabf2..755ac664 100644 --- a/gns3server/schemas/nat_nodes.py +++ b/gns3server/schemas/compute/nat_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, Union, List from enum import Enum from uuid import UUID -from .nodes import NodeStatus +from ..common import NodeStatus class HostInterfaceType(Enum): diff --git a/gns3server/schemas/nios.py b/gns3server/schemas/compute/nios.py similarity index 96% rename from gns3server/schemas/nios.py rename to gns3server/schemas/compute/nios.py index 10514a3b..75ab4b5b 100644 --- a/gns3server/schemas/nios.py +++ b/gns3server/schemas/compute/nios.py @@ -16,9 +16,8 @@ from pydantic import BaseModel, Field -from typing import Optional, Union, Generic +from typing import Optional from enum import Enum -from uuid import UUID class UDPNIOType(Enum): diff --git a/gns3server/schemas/qemu_nodes.py b/gns3server/schemas/compute/qemu_nodes.py similarity index 99% rename from gns3server/schemas/qemu_nodes.py rename to gns3server/schemas/compute/qemu_nodes.py index 6433f5d9..de464fef 100644 --- a/gns3server/schemas/qemu_nodes.py +++ b/gns3server/schemas/compute/qemu_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, List from enum import Enum from uuid import UUID -from .nodes import CustomAdapter, NodeStatus +from ..common import NodeStatus, CustomAdapter class QemuPlatform(str, Enum): diff --git a/gns3server/schemas/virtualbox_nodes.py b/gns3server/schemas/compute/virtualbox_nodes.py similarity index 98% rename from gns3server/schemas/virtualbox_nodes.py rename to gns3server/schemas/compute/virtualbox_nodes.py index 72fc1144..53f93975 100644 --- a/gns3server/schemas/virtualbox_nodes.py +++ b/gns3server/schemas/compute/virtualbox_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, List from enum import Enum from uuid import UUID -from .nodes import NodeStatus, CustomAdapter +from ..common import NodeStatus, CustomAdapter class VirtualBoxConsoleType(str, Enum): diff --git a/gns3server/schemas/vmware_nodes.py b/gns3server/schemas/compute/vmware_nodes.py similarity index 98% rename from gns3server/schemas/vmware_nodes.py rename to gns3server/schemas/compute/vmware_nodes.py index 4a3f2fc9..a2fb26f2 100644 --- a/gns3server/schemas/vmware_nodes.py +++ b/gns3server/schemas/compute/vmware_nodes.py @@ -19,7 +19,7 @@ from typing import Optional, List from enum import Enum from uuid import UUID -from .nodes import NodeStatus, CustomAdapter +from ..common import NodeStatus, CustomAdapter class VMwareConsoleType(str, Enum): diff --git a/gns3server/schemas/vpcs_nodes.py b/gns3server/schemas/compute/vpcs_nodes.py similarity index 97% rename from gns3server/schemas/vpcs_nodes.py rename to gns3server/schemas/compute/vpcs_nodes.py index 324be1a7..6373182c 100644 --- a/gns3server/schemas/vpcs_nodes.py +++ b/gns3server/schemas/compute/vpcs_nodes.py @@ -19,7 +19,7 @@ from typing import Optional from enum import Enum from uuid import UUID -from .nodes import NodeStatus, CustomAdapter +from ..common import NodeStatus class ConsoleType(str, Enum): diff --git a/gns3server/schemas/config.py b/gns3server/schemas/config.py index c30dada7..d692cf9e 100644 --- a/gns3server/schemas/config.py +++ b/gns3server/schemas/config.py @@ -15,7 +15,7 @@ # along with this program. If not, see . from enum import Enum -from pydantic import BaseModel, Field, SecretStr, FilePath, validator +from pydantic import BaseModel, Field, SecretStr, FilePath, DirectoryPath, validator from typing import List @@ -113,7 +113,7 @@ class ServerSettings(BaseModel): protocol: ServerProtocol = ServerProtocol.http host: str = "0.0.0.0" port: int = Field(3080, gt=0, le=65535) - secrets_dir: str = None + secrets_dir: DirectoryPath = None certfile: FilePath = None certkey: FilePath = None enable_ssl: bool = False @@ -167,7 +167,7 @@ class ServerSettings(BaseModel): if v is True: if "user" not in values or not values["user"]: - raise ValueError("HTTP authentication is enabled but no username is configured") + raise ValueError("HTTP authentication is enabled but user is not configured") return v @validator("enable_ssl") diff --git a/gns3server/handlers/api/controller/link_handler.py b/gns3server/schemas/controller/__init__.py similarity index 100% rename from gns3server/handlers/api/controller/link_handler.py rename to gns3server/schemas/controller/__init__.py diff --git a/gns3server/schemas/base.py b/gns3server/schemas/controller/base.py similarity index 100% rename from gns3server/schemas/base.py rename to gns3server/schemas/controller/base.py diff --git a/gns3server/schemas/capabilities.py b/gns3server/schemas/controller/capabilities.py similarity index 100% rename from gns3server/schemas/capabilities.py rename to gns3server/schemas/controller/capabilities.py diff --git a/gns3server/schemas/computes.py b/gns3server/schemas/controller/computes.py similarity index 100% rename from gns3server/schemas/computes.py rename to gns3server/schemas/controller/computes.py diff --git a/gns3server/schemas/drawings.py b/gns3server/schemas/controller/drawings.py similarity index 100% rename from gns3server/schemas/drawings.py rename to gns3server/schemas/controller/drawings.py diff --git a/gns3server/schemas/gns3vm.py b/gns3server/schemas/controller/gns3vm.py similarity index 100% rename from gns3server/schemas/gns3vm.py rename to gns3server/schemas/controller/gns3vm.py diff --git a/gns3server/schemas/iou_license.py b/gns3server/schemas/controller/iou_license.py similarity index 100% rename from gns3server/schemas/iou_license.py rename to gns3server/schemas/controller/iou_license.py diff --git a/gns3server/schemas/filters.py b/gns3server/schemas/controller/labels.py similarity index 51% rename from gns3server/schemas/filters.py rename to gns3server/schemas/controller/labels.py index 52bafdd0..2eb77026 100644 --- a/gns3server/schemas/filters.py +++ b/gns3server/schemas/controller/labels.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python # -# Copyright (C) 2020 GNS3 Technologies Inc. +# Copyright (C) 2021 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 @@ -14,3 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + +from pydantic import BaseModel, Field +from typing import Optional, Union + + +class Label(BaseModel): + """ + Label data. + """ + + text: str + style: Optional[Union[str, None]] = Field(None, description="SVG style attribute. Apply default style if null") + x: Optional[Union[int, None]] = Field(None, description="Relative X position of the label. Center it if null") + y: Optional[int] = Field(None, description="Relative Y position of the label") + rotation: Optional[int] = Field(None, ge=-359, le=360, description="Rotation of the label") diff --git a/gns3server/schemas/links.py b/gns3server/schemas/controller/links.py similarity index 98% rename from gns3server/schemas/links.py rename to gns3server/schemas/controller/links.py index 98efba28..3a37ccbc 100644 --- a/gns3server/schemas/links.py +++ b/gns3server/schemas/controller/links.py @@ -19,7 +19,7 @@ from typing import List, Optional from enum import Enum from uuid import UUID -from .common import Label +from .labels import Label class LinkNode(BaseModel): diff --git a/gns3server/schemas/nodes.py b/gns3server/schemas/controller/nodes.py similarity index 70% rename from gns3server/schemas/nodes.py rename to gns3server/schemas/controller/nodes.py index 3a6f2802..1e59fef1 100644 --- a/gns3server/schemas/nodes.py +++ b/gns3server/schemas/controller/nodes.py @@ -14,12 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, validator from typing import List, Optional, Union from enum import Enum -from uuid import UUID +from uuid import UUID, uuid4 -from .common import Label +from .labels import Label +from ..common import ConsoleType, NodeStatus, CustomAdapter class NodeType(str, Enum): @@ -74,39 +75,6 @@ class DataLinkType(str, Enum): ppp = "DLT_PPP_SERIAL" -class ConsoleType(str, Enum): - """ - Supported console types. - """ - - vnc = "vnc" - telnet = "telnet" - http = "http" - https = "https" - spice = "spice" - spice_agent = "spice+agent" - none = "none" - - -class AuxType(str, Enum): - """ - Supported auxiliary console types. - """ - - telnet = "telnet" - none = "none" - - -class NodeStatus(str, Enum): - """ - Supported node statuses. - """ - - stopped = "stopped" - started = "started" - suspended = "suspended" - - class NodeCapture(BaseModel): """ Node capture data. @@ -116,17 +84,6 @@ class NodeCapture(BaseModel): data_link_type: Optional[DataLinkType] = None -class CustomAdapter(BaseModel): - """ - Custom adapter data. - """ - - adapter_number: int - port_name: Optional[str] = None - adapter_type: Optional[str] = None - mac_address: Optional[str] = Field(None, regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$") - - class NodePort(BaseModel): """ Node port data. @@ -142,7 +99,7 @@ class NodePort(BaseModel): mac_address: Union[str, None] = Field(None, regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$") -class Node(BaseModel): +class NodeBase(BaseModel): """ Node data. """ @@ -150,44 +107,56 @@ class Node(BaseModel): compute_id: Union[UUID, str] name: str node_type: NodeType - project_id: Optional[UUID] = None + node_id: Optional[UUID] = None - template_id: Optional[UUID] = Field( - None, description="Template UUID from which the node has been created. Read only" - ) - node_directory: Optional[str] = Field(None, description="Working directory of the node. Read only") - command_line: Optional[str] = Field(None, description="Command line use to start the node") + console: Optional[int] = Field(None, gt=0, le=65535, description="Console TCP port") - console_host: Optional[str] = Field( - None, - description="Console host. Warning if the host is 0.0.0.0 or :: (listen on all interfaces) you need to use the same address you use to connect to the controller", - ) + console_type: Optional[ConsoleType] = None console_auto_start: Optional[bool] = Field( - None, description="Automatically start the console when the node has started" + False, description="Automatically start the console when the node has started" ) aux: Optional[int] = Field(None, gt=0, le=65535, description="Auxiliary console TCP port") aux_type: Optional[ConsoleType] - properties: Optional[dict] = Field(None, description="Properties specific to an emulator") - status: Optional[NodeStatus] = None + properties: Optional[dict] = Field(default_factory=dict, description="Properties specific to an emulator") + label: Optional[Label] = None symbol: Optional[str] = None - width: Optional[int] = Field(None, description="Width of the node (Read only)") - height: Optional[int] = Field(None, description="Height of the node (Read only)") - x: Optional[int] = None - y: Optional[int] = None - z: Optional[int] = None - locked: Optional[bool] = Field(None, description="Whether the element locked or not") + + x: Optional[int] = 0 + y: Optional[int] = 0 + z: Optional[int] = 1 + locked: Optional[bool] = Field(False, description="Whether the element locked or not") port_name_format: Optional[str] = Field( - None, description="Formatting for port name {0} will be replace by port number" + None, descript_port_name_formation="Formatting for port name {0} will be replace by port number" ) port_segment_size: Optional[int] = Field(None, description="Size of the port segment") first_port_name: Optional[str] = Field(None, description="Name of the first port") custom_adapters: Optional[List[CustomAdapter]] = None - ports: Optional[List[NodePort]] = Field(None, description="List of node ports (read only)") + + @validator("port_name_format", pre=True, always=True) + def default_port_name_format(cls, v, values): + if v is None: + if "node_type" in values and values["node_type"] == NodeType.iou: + return "Ethernet{segment0}/{port0}" + return "Ethernet{0}" + return v + + @validator("port_segment_size", pre=True, always=True) + def default_port_segment_size(cls, v, values): + if v is None: + if "node_type" in values and values["node_type"] == NodeType.iou: + return 4 + return 0 + return v -class NodeUpdate(Node): +class NodeCreate(NodeBase): + + node_id: UUID = Field(default_factory=uuid4) + + +class NodeUpdate(NodeBase): """ Data to update a node. """ @@ -197,6 +166,23 @@ class NodeUpdate(Node): node_type: Optional[NodeType] = None +class Node(NodeBase): + + template_id: Optional[UUID] = Field(None, + description="Template UUID from which the node has been created. Read only") + project_id: Optional[UUID] = None + node_directory: Optional[str] = Field(None, description="Working directory of the node. Read only") + status: Optional[NodeStatus] = Field(None, description="Node status. Read only") + command_line: Optional[str] = Field(None, description="Command line use to start the node. Read only") + width: Optional[int] = Field(None, description="Width of the node. Read only") + height: Optional[int] = Field(None, description="Height of the node. Read only") + ports: Optional[List[NodePort]] = Field(None, description="List of node ports. Read only") + console_host: Optional[str] = Field( + None, + description="Console host. Warning if the host is 0.0.0.0 or :: (listen on all interfaces) you need to use the same address you use to connect to the controller", + ) + + class NodeDuplicate(BaseModel): """ Data to duplicate a node. diff --git a/gns3server/schemas/projects.py b/gns3server/schemas/controller/projects.py similarity index 100% rename from gns3server/schemas/projects.py rename to gns3server/schemas/controller/projects.py diff --git a/gns3server/schemas/snapshots.py b/gns3server/schemas/controller/snapshots.py similarity index 100% rename from gns3server/schemas/snapshots.py rename to gns3server/schemas/controller/snapshots.py diff --git a/gns3server/schemas/templates.py b/gns3server/schemas/controller/templates/__init__.py similarity index 94% rename from gns3server/schemas/templates.py rename to gns3server/schemas/controller/templates/__init__.py index f9de0b03..74866b2e 100644 --- a/gns3server/schemas/templates.py +++ b/gns3server/schemas/controller/templates/__init__.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2020 GNS3 Technologies Inc. +# Copyright (C) 2021 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 @@ -19,8 +19,8 @@ from typing import Optional, Union from enum import Enum from uuid import UUID -from .nodes import NodeType -from .base import DateTimeModelMixin +from ..nodes import NodeType +from ..base import DateTimeModelMixin class Category(str, Enum): diff --git a/gns3server/schemas/cloud_templates.py b/gns3server/schemas/controller/templates/cloud_templates.py similarity index 90% rename from gns3server/schemas/cloud_templates.py rename to gns3server/schemas/controller/templates/cloud_templates.py index 6ae199f1..37fd1942 100644 --- a/gns3server/schemas/cloud_templates.py +++ b/gns3server/schemas/controller/templates/cloud_templates.py @@ -15,8 +15,13 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .cloud_nodes import EthernetPort, TAPPort, UDPPort, CloudConsoleType +from . import Category, TemplateBase +from gns3server.schemas.compute.cloud_nodes import ( + EthernetPort, + TAPPort, + UDPPort, + CloudConsoleType +) from pydantic import Field from typing import Optional, Union, List diff --git a/gns3server/schemas/docker_templates.py b/gns3server/schemas/controller/templates/docker_templates.py similarity index 95% rename from gns3server/schemas/docker_templates.py rename to gns3server/schemas/controller/templates/docker_templates.py index 48f16e1a..ceb05040 100644 --- a/gns3server/schemas/docker_templates.py +++ b/gns3server/schemas/controller/templates/docker_templates.py @@ -15,9 +15,8 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .nodes import CustomAdapter -from .docker_nodes import ConsoleType, AuxType +from . import Category, TemplateBase +from ...common import ConsoleType, AuxType, CustomAdapter from pydantic import Field from typing import Optional, List diff --git a/gns3server/schemas/dynamips_templates.py b/gns3server/schemas/controller/templates/dynamips_templates.py similarity index 98% rename from gns3server/schemas/dynamips_templates.py rename to gns3server/schemas/controller/templates/dynamips_templates.py index 308a90f5..9598505e 100644 --- a/gns3server/schemas/dynamips_templates.py +++ b/gns3server/schemas/controller/templates/dynamips_templates.py @@ -14,8 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from .templates import Category, TemplateBase -from .dynamips_nodes import ( +from . import Category, TemplateBase + +from gns3server.schemas.compute.dynamips_nodes import ( DynamipsConsoleType, DynamipsPlatform, DynamipsAdapters, diff --git a/gns3server/schemas/ethernet_hub_templates.py b/gns3server/schemas/controller/templates/ethernet_hub_templates.py similarity index 92% rename from gns3server/schemas/ethernet_hub_templates.py rename to gns3server/schemas/controller/templates/ethernet_hub_templates.py index 947d41db..7b61cd01 100644 --- a/gns3server/schemas/ethernet_hub_templates.py +++ b/gns3server/schemas/controller/templates/ethernet_hub_templates.py @@ -14,9 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -from .templates import Category, TemplateBase -from .ethernet_hub_nodes import EthernetHubPort +from . import Category, TemplateBase +from gns3server.schemas.compute.ethernet_hub_nodes import EthernetHubPort from pydantic import Field from typing import Optional, List diff --git a/gns3server/schemas/ethernet_switch_templates.py b/gns3server/schemas/controller/templates/ethernet_switch_templates.py similarity index 94% rename from gns3server/schemas/ethernet_switch_templates.py rename to gns3server/schemas/controller/templates/ethernet_switch_templates.py index 51373e31..2e42aaa2 100644 --- a/gns3server/schemas/ethernet_switch_templates.py +++ b/gns3server/schemas/controller/templates/ethernet_switch_templates.py @@ -15,8 +15,8 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .ethernet_switch_nodes import EthernetSwitchPort +from . import Category, TemplateBase +from gns3server.schemas.compute.ethernet_switch_nodes import EthernetSwitchPort from pydantic import Field from typing import Optional, List diff --git a/gns3server/schemas/iou_templates.py b/gns3server/schemas/controller/templates/iou_templates.py similarity index 95% rename from gns3server/schemas/iou_templates.py rename to gns3server/schemas/controller/templates/iou_templates.py index 0c7100b9..f7bba8d4 100644 --- a/gns3server/schemas/iou_templates.py +++ b/gns3server/schemas/controller/templates/iou_templates.py @@ -15,8 +15,8 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .iou_nodes import ConsoleType +from . import Category, TemplateBase +from gns3server.schemas.compute.iou_nodes import ConsoleType from pydantic import Field from typing import Optional diff --git a/gns3server/schemas/qemu_templates.py b/gns3server/schemas/controller/templates/qemu_templates.py similarity index 98% rename from gns3server/schemas/qemu_templates.py rename to gns3server/schemas/controller/templates/qemu_templates.py index 8ab0d452..f69eb23a 100644 --- a/gns3server/schemas/qemu_templates.py +++ b/gns3server/schemas/controller/templates/qemu_templates.py @@ -15,8 +15,8 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .qemu_nodes import ( +from . import Category, TemplateBase +from gns3server.schemas.compute.qemu_nodes import ( QemuConsoleType, QemuPlatform, QemuAdapterType, diff --git a/gns3server/schemas/virtualbox_templates.py b/gns3server/schemas/controller/templates/virtualbox_templates.py similarity index 93% rename from gns3server/schemas/virtualbox_templates.py rename to gns3server/schemas/controller/templates/virtualbox_templates.py index 967bea3d..82cd63f4 100644 --- a/gns3server/schemas/virtualbox_templates.py +++ b/gns3server/schemas/controller/templates/virtualbox_templates.py @@ -14,8 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from .templates import Category, TemplateBase -from .virtualbox_nodes import VirtualBoxConsoleType, VirtualBoxAdapterType, VirtualBoxOnCloseAction, CustomAdapter +from . import Category, TemplateBase +from gns3server.schemas.compute.virtualbox_nodes import ( + VirtualBoxConsoleType, + VirtualBoxAdapterType, + VirtualBoxOnCloseAction, + CustomAdapter +) from pydantic import Field from typing import Optional, List diff --git a/gns3server/schemas/vmware_templates.py b/gns3server/schemas/controller/templates/vmware_templates.py similarity index 93% rename from gns3server/schemas/vmware_templates.py rename to gns3server/schemas/controller/templates/vmware_templates.py index e0d6cef0..a8d604d8 100644 --- a/gns3server/schemas/vmware_templates.py +++ b/gns3server/schemas/controller/templates/vmware_templates.py @@ -15,8 +15,13 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .vmware_nodes import VMwareConsoleType, VMwareAdapterType, VMwareOnCloseAction, CustomAdapter +from . import Category, TemplateBase +from gns3server.schemas.compute.vmware_nodes import ( + VMwareConsoleType, + VMwareAdapterType, + VMwareOnCloseAction, + CustomAdapter +) from pydantic import Field from typing import Optional, List diff --git a/gns3server/schemas/vpcs_templates.py b/gns3server/schemas/controller/templates/vpcs_templates.py similarity index 92% rename from gns3server/schemas/vpcs_templates.py rename to gns3server/schemas/controller/templates/vpcs_templates.py index 18291c89..f75009ce 100644 --- a/gns3server/schemas/vpcs_templates.py +++ b/gns3server/schemas/controller/templates/vpcs_templates.py @@ -15,8 +15,8 @@ # along with this program. If not, see . -from .templates import Category, TemplateBase -from .vpcs_nodes import ConsoleType +from . import Category, TemplateBase +from gns3server.schemas.compute.vpcs_nodes import ConsoleType from pydantic import Field from typing import Optional diff --git a/gns3server/schemas/tokens.py b/gns3server/schemas/controller/tokens.py similarity index 100% rename from gns3server/schemas/tokens.py rename to gns3server/schemas/controller/tokens.py diff --git a/gns3server/schemas/topology.py b/gns3server/schemas/controller/topology.py similarity index 100% rename from gns3server/schemas/topology.py rename to gns3server/schemas/controller/topology.py diff --git a/gns3server/schemas/users.py b/gns3server/schemas/controller/users.py similarity index 100% rename from gns3server/schemas/users.py rename to gns3server/schemas/controller/users.py diff --git a/gns3server/services/authentication.py b/gns3server/services/authentication.py index e2a4b64f..021db36e 100644 --- a/gns3server/services/authentication.py +++ b/gns3server/services/authentication.py @@ -21,7 +21,7 @@ from passlib.context import CryptContext from typing import Optional from fastapi import HTTPException, status -from gns3server.schemas.tokens import TokenData +from gns3server.schemas.controller.tokens import TokenData from gns3server.config import Config from pydantic import ValidationError diff --git a/tests/conftest.py b/tests/conftest.py index 072c124e..7996955c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -344,13 +344,27 @@ def run_around_tests(monkeypatch, config, port_manager):#port_manager, controlle module._instance = None config.settings.Controller.jwt_secret_key = DEFAULT_JWT_SECRET_KEY - config.settings.Server.secrets_dir = os.path.join(tmppath, 'secrets') - os.makedirs(os.path.join(tmppath, 'projects')) - config.settings.Server.projects_path = os.path.join(tmppath, 'projects') - config.settings.Server.symbols_path = os.path.join(tmppath, 'symbols') - config.settings.Server.images_path = os.path.join(tmppath, 'images') - config.settings.Server.appliances_path = os.path.join(tmppath, 'appliances') + secrets_dir = os.path.join(tmppath, 'secrets') + os.makedirs(secrets_dir) + config.settings.Server.secrets_dir = secrets_dir + + projects_dir = os.path.join(tmppath, 'projects') + os.makedirs(projects_dir) + config.settings.Server.projects_path = projects_dir + + symbols_dir = os.path.join(tmppath, 'symbols') + os.makedirs(symbols_dir) + config.settings.Server.symbols_path = symbols_dir + + images_dir = os.path.join(tmppath, 'images') + os.makedirs(images_dir) + config.settings.Server.images_path = images_dir + + appliances_dir = os.path.join(tmppath, 'appliances') + os.makedirs(appliances_dir) + config.settings.Server.appliances_path = appliances_dir + config.settings.Server.ubridge_path = os.path.join(tmppath, 'bin', 'ubridge') config.settings.Server.local = True config.settings.Server.enable_http_auth = False