From 44074ff7c9439b879b5a937b7fb5a1760a31fd71 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 17 Apr 2021 18:36:32 +0930 Subject: [PATCH] Some cleaning. --- gns3server/api/routes/controller/links.py | 4 +- .../compute/builtin/nodes/ethernet_hub.py | 7 ++- .../compute/builtin/nodes/ethernet_switch.py | 7 ++- .../dynamips/nios/nio_generic_ethernet.py | 5 +- .../dynamips/nios/nio_linux_ethernet.py | 5 +- gns3server/compute/dynamips/nios/nio_tap.py | 5 +- gns3server/compute/dynamips/nios/nio_udp.py | 7 ++- gns3server/compute/dynamips/nios/nio_unix.py | 6 +- gns3server/compute/dynamips/nios/nio_vde.py | 6 +- gns3server/compute/nios/nio_ethernet.py | 5 +- gns3server/compute/nios/nio_tap.py | 5 +- gns3server/compute/nios/nio_udp.py | 7 ++- gns3server/compute/project.py | 6 +- gns3server/controller/appliance.py | 3 +- gns3server/controller/node.py | 55 ++++++------------- gns3server/controller/notification.py | 27 +-------- gns3server/controller/project.py | 5 +- gns3server/controller/snapshot.py | 8 +-- gns3server/controller/topology.py | 3 +- gns3server/schemas/__init__.py | 2 +- gns3server/schemas/controller/links.py | 41 ++++++++++---- gns3server/utils/cpu_percent.py | 2 +- 22 files changed, 122 insertions(+), 99 deletions(-) diff --git a/gns3server/api/routes/controller/links.py b/gns3server/api/routes/controller/links.py index 7b91460d..5ae0fe03 100644 --- a/gns3server/api/routes/controller/links.py +++ b/gns3server/api/routes/controller/links.py @@ -71,7 +71,7 @@ async def get_links(project_id: UUID): 409: {"model": schemas.ErrorMessage, "description": "Could not create link"}, }, ) -async def create_link(project_id: UUID, link_data: schemas.Link): +async def create_link(project_id: UUID, link_data: schemas.LinkCreate): """ Create a new link. """ @@ -116,7 +116,7 @@ async def get_link(link: Link = Depends(dep_link)): @router.put("/{link_id}", response_model=schemas.Link, response_model_exclude_unset=True) -async def update_link(link_data: schemas.Link, link: Link = Depends(dep_link)): +async def update_link(link_data: schemas.LinkUpdate, link: Link = Depends(dep_link)): """ Update a link. """ diff --git a/gns3server/compute/builtin/nodes/ethernet_hub.py b/gns3server/compute/builtin/nodes/ethernet_hub.py index ca6d316a..c2ca8f61 100644 --- a/gns3server/compute/builtin/nodes/ethernet_hub.py +++ b/gns3server/compute/builtin/nodes/ethernet_hub.py @@ -40,7 +40,12 @@ class EthernetHub(BaseNode): def __json__(self): - return {"name": self.name, "usage": self.usage, "node_id": self.id, "project_id": self.project.id} + return { + "name": self.name, + "usage": self.usage, + "node_id": self.id, + "project_id": self.project.id + } async def create(self): """ diff --git a/gns3server/compute/builtin/nodes/ethernet_switch.py b/gns3server/compute/builtin/nodes/ethernet_switch.py index a16c64b3..22aa8647 100644 --- a/gns3server/compute/builtin/nodes/ethernet_switch.py +++ b/gns3server/compute/builtin/nodes/ethernet_switch.py @@ -40,7 +40,12 @@ class EthernetSwitch(BaseNode): def __json__(self): - return {"name": self.name, "usage": self.usage, "node_id": self.id, "project_id": self.project.id} + return { + "name": self.name, + "usage": self.usage, + "node_id": self.id, + "project_id": self.project.id + } async def create(self): """ diff --git a/gns3server/compute/dynamips/nios/nio_generic_ethernet.py b/gns3server/compute/dynamips/nios/nio_generic_ethernet.py index 519ac7dc..94a1c0c7 100644 --- a/gns3server/compute/dynamips/nios/nio_generic_ethernet.py +++ b/gns3server/compute/dynamips/nios/nio_generic_ethernet.py @@ -67,4 +67,7 @@ class NIOGenericEthernet(NIO): def __json__(self): - return {"type": "nio_generic_ethernet", "ethernet_device": self._ethernet_device} + return { + "type": "nio_generic_ethernet", + "ethernet_device": self._ethernet_device + } diff --git a/gns3server/compute/dynamips/nios/nio_linux_ethernet.py b/gns3server/compute/dynamips/nios/nio_linux_ethernet.py index 2ced9d83..1d76eeee 100644 --- a/gns3server/compute/dynamips/nios/nio_linux_ethernet.py +++ b/gns3server/compute/dynamips/nios/nio_linux_ethernet.py @@ -66,4 +66,7 @@ class NIOLinuxEthernet(NIO): def __json__(self): - return {"type": "nio_linux_ethernet", "ethernet_device": self._ethernet_device} + return { + "type": "nio_linux_ethernet", + "ethernet_device": self._ethernet_device + } diff --git a/gns3server/compute/dynamips/nios/nio_tap.py b/gns3server/compute/dynamips/nios/nio_tap.py index 4e27dcce..cf93eeeb 100644 --- a/gns3server/compute/dynamips/nios/nio_tap.py +++ b/gns3server/compute/dynamips/nios/nio_tap.py @@ -60,4 +60,7 @@ class NIOTAP(NIO): def __json__(self): - return {"type": "nio_tap", "tap_device": self._tap_device} + return { + "type": "nio_tap", + "tap_device": self._tap_device + } diff --git a/gns3server/compute/dynamips/nios/nio_udp.py b/gns3server/compute/dynamips/nios/nio_udp.py index 2988e858..52a29d61 100644 --- a/gns3server/compute/dynamips/nios/nio_udp.py +++ b/gns3server/compute/dynamips/nios/nio_udp.py @@ -128,4 +128,9 @@ class NIOUDP(NIO): def __json__(self): - return {"type": "nio_udp", "lport": self._lport, "rport": self._rport, "rhost": self._rhost} + return { + "type": "nio_udp", + "lport": self._lport, + "rport": self._rport, + "rhost": self._rhost + } diff --git a/gns3server/compute/dynamips/nios/nio_unix.py b/gns3server/compute/dynamips/nios/nio_unix.py index dfc9065b..44010b27 100644 --- a/gns3server/compute/dynamips/nios/nio_unix.py +++ b/gns3server/compute/dynamips/nios/nio_unix.py @@ -81,4 +81,8 @@ class NIOUNIX(NIO): def __json__(self): - return {"type": "nio_unix", "local_file": self._local_file, "remote_file": self._remote_file} + return { + "type": "nio_unix", + "local_file": self._local_file, + "remote_file": self._remote_file + } diff --git a/gns3server/compute/dynamips/nios/nio_vde.py b/gns3server/compute/dynamips/nios/nio_vde.py index ef2d921f..5971b892 100644 --- a/gns3server/compute/dynamips/nios/nio_vde.py +++ b/gns3server/compute/dynamips/nios/nio_vde.py @@ -81,4 +81,8 @@ class NIOVDE(NIO): def __json__(self): - return {"type": "nio_vde", "local_file": self._local_file, "control_file": self._control_file} + return { + "type": "nio_vde", + "local_file": self._local_file, + "control_file": self._control_file + } diff --git a/gns3server/compute/nios/nio_ethernet.py b/gns3server/compute/nios/nio_ethernet.py index a80ab5e2..1f521a69 100644 --- a/gns3server/compute/nios/nio_ethernet.py +++ b/gns3server/compute/nios/nio_ethernet.py @@ -50,4 +50,7 @@ class NIOEthernet(NIO): def __json__(self): - return {"type": "nio_ethernet", "ethernet_device": self._ethernet_device} + return { + "type": "nio_ethernet", + "ethernet_device": self._ethernet_device + } diff --git a/gns3server/compute/nios/nio_tap.py b/gns3server/compute/nios/nio_tap.py index 51deefe2..fee2d3d4 100644 --- a/gns3server/compute/nios/nio_tap.py +++ b/gns3server/compute/nios/nio_tap.py @@ -50,4 +50,7 @@ class NIOTAP(NIO): def __json__(self): - return {"type": "nio_tap", "tap_device": self._tap_device} + return { + "type": "nio_tap", + "tap_device": self._tap_device + } diff --git a/gns3server/compute/nios/nio_udp.py b/gns3server/compute/nios/nio_udp.py index 68fd2a8a..b9a87caf 100644 --- a/gns3server/compute/nios/nio_udp.py +++ b/gns3server/compute/nios/nio_udp.py @@ -74,4 +74,9 @@ class NIOUDP(NIO): def __json__(self): - return {"type": "nio_udp", "lport": self._lport, "rport": self._rport, "rhost": self._rhost} + return { + "type": "nio_udp", + "lport": self._lport, + "rport": self._rport, + "rhost": self._rhost + } diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index c1a44225..4f224f79 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -79,7 +79,11 @@ class Project: def __json__(self): - return {"name": self._name, "project_id": self._id, "variables": self._variables} + return { + "name": self._name, + "project_id": self._id, + "variables": self._variables + } def is_local(self): diff --git a/gns3server/controller/appliance.py b/gns3server/controller/appliance.py index d96d436b..47ac24cf 100644 --- a/gns3server/controller/appliance.py +++ b/gns3server/controller/appliance.py @@ -17,14 +17,15 @@ import copy import uuid - import logging log = logging.getLogger(__name__) class Appliance: + def __init__(self, appliance_id, data, builtin=True): + if appliance_id is None: self._id = str(uuid.uuid4()) elif isinstance(appliance_id, uuid.UUID): diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 80e8c5d6..0032b49a 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -390,7 +390,6 @@ class Node: data["node_id"] = self._id if self._node_type == "docker": timeout = None - else: timeout = 1200 trial = 0 @@ -543,7 +542,7 @@ class Node: if self.custom_adapters: data["custom_adapters"] = self.custom_adapters - # None properties are not be send. Because it can mean the emulator doesn't support it + # None properties are not be sent because it can mean the emulator doesn't support it for key in list(data.keys()): if data[key] is None or data[key] is {} or key in self.CONTROLLER_ONLY_PROPERTIES: del data[key] @@ -628,7 +627,7 @@ class Node: async def put(self, path, data=None, **kwargs): """ - HTTP post on the node + HTTP put on the node """ if path is None: path = f"/projects/{self._project.id}/{self._node_type}/nodes/{self._id}" @@ -780,11 +779,10 @@ class Node: def __json__(self, topology_dump=False): """ - :param topology_dump: Filter to keep only properties require for saving on disk + :param topology_dump: Filter to keep only properties required for saving on disk """ - if topology_dump: - return { + topology = { "compute_id": str(self._compute.id), "node_id": self._id, "node_type": self._node_type, @@ -808,35 +806,18 @@ class Node: "port_segment_size": self._port_segment_size, "first_port_name": self._first_port_name, "custom_adapters": self._custom_adapters, - } - return { - "compute_id": str(self._compute.id), - "project_id": self._project.id, - "node_id": self._id, - "template_id": self._template_id, - "node_type": self._node_type, - "node_directory": self._node_directory, - "name": self._name, - "console": self._console, - "console_host": str(self._compute.console_host), - "console_type": self._console_type, - "aux": self._aux, - "aux_type": self._aux_type, - "console_auto_start": self._console_auto_start, - "command_line": self._command_line, - "properties": self._properties, - "status": self._status, - "label": self._label, - "x": self._x, - "y": self._y, - "z": self._z, - "locked": self._locked, - "width": self._width, - "height": self._height, - "symbol": self._symbol, - "port_name_format": self._port_name_format, - "port_segment_size": self._port_segment_size, - "first_port_name": self._first_port_name, - "custom_adapters": self._custom_adapters, - "ports": [port.__json__() for port in self.ports], } + + if topology_dump: + return topology + + additional_data = { + "project_id": self._project.id, + "command_line": self._command_line, + "status": self._status, + "console_host": str(self._compute.console_host), + "node_directory": self._node_directory, + "ports": [port.__json__() for port in self.ports] + } + topology.update(additional_data) + return topology diff --git a/gns3server/controller/notification.py b/gns3server/controller/notification.py index 9b9c3c5a..c7aaf0b4 100644 --- a/gns3server/controller/notification.py +++ b/gns3server/controller/notification.py @@ -28,6 +28,7 @@ class Notification: """ def __init__(self, controller): + self._controller = controller self._project_listeners = {} self._controller_listeners = set() @@ -71,19 +72,6 @@ class Notification: :param event: Event to send """ - # If use in tests for documentation we save a sample - if os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1": - os.makedirs("docs/api/notifications", exist_ok=True) - try: - import json - - data = json.dumps(event, indent=4, sort_keys=True) - if "MagicMock" not in data: - with open(os.path.join("docs/api/notifications", action + ".json"), "w+") as f: - f.write(data) - except TypeError: # If we receive a mock as an event it will raise TypeError when using json dump - pass - for controller_listener in self._controller_listeners: controller_listener.put_nowait((action, event, {})) @@ -127,19 +115,6 @@ class Notification: :param event: Event to send """ - # If use in tests for documentation we save a sample - if os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1": - os.makedirs("docs/api/notifications", exist_ok=True) - try: - import json - - data = json.dumps(event, indent=4, sort_keys=True) - if "MagicMock" not in data: - with open(os.path.join("docs/api/notifications", action + ".json"), "w+") as f: - f.write(data) - except TypeError: # If we receive a mock as an event it will raise TypeError when using json dump - pass - if "project_id" in event or project_id: self._send_event_to_project(event.get("project_id", project_id), action, event) else: diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 8a5c7d3c..38131609 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -633,7 +633,7 @@ class Project: def _get_closed_data(self, section, id_key): """ Get the data for a project from the .gns3 when - the project is close + the project is closed :param section: The section name in the .gns3 :param id_key: The key for the element unique id @@ -1117,7 +1117,7 @@ class Project: try: topo = project_to_topology(self) path = self._topology_file() - log.debug("Write %s", path) + log.debug(f"Write topology file '{path}'") with open(path + ".tmp", "w+", encoding="utf-8") as f: json.dump(topo, f, indent=4, sort_keys=True) shutil.move(path + ".tmp", path) @@ -1176,6 +1176,7 @@ class Project: :param z: Z position :returns: New node """ + if node.status != "stopped" and not node.is_always_running(): raise ControllerError("Cannot duplicate node data while the node is running") diff --git a/gns3server/controller/snapshot.py b/gns3server/controller/snapshot.py index e0af79af..35883f93 100644 --- a/gns3server/controller/snapshot.py +++ b/gns3server/controller/snapshot.py @@ -36,10 +36,6 @@ import logging log = logging.getLogger(__name__) -# The string use to extract the date from the filename -FILENAME_TIME_FORMAT = "%d%m%y_%H%M%S" - - class Snapshot: """ A snapshot object @@ -59,7 +55,7 @@ class Snapshot: filename = ( self._name + "_" - + datetime.utcfromtimestamp(self._created_at).replace(tzinfo=None).strftime(FILENAME_TIME_FORMAT) + + datetime.utcfromtimestamp(self._created_at).replace(tzinfo=None).strftime("%d%m%y_%H%M%S") + ".gns3project" ) else: @@ -67,7 +63,7 @@ class Snapshot: datestring = filename.replace(self._name + "_", "").split(".")[0] try: self._created_at = ( - datetime.strptime(datestring, FILENAME_TIME_FORMAT).replace(tzinfo=timezone.utc).timestamp() + datetime.strptime(datestring, "%d%m%y_%H%M%S").replace(tzinfo=timezone.utc).timestamp() ) except ValueError: self._created_at = datetime.utcnow().timestamp() diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index fa2a54d6..7b3134e8 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -121,7 +121,8 @@ def load_topology(path): """ Open a topology file, patch it for last GNS3 release and return it """ - log.debug("Read topology %s", path) + + log.debug(f"Read topology {path}") try: with open(path, encoding="utf-8") as f: topo = json.load(f) diff --git a/gns3server/schemas/__init__.py b/gns3server/schemas/__init__.py index 8ff9d6f0..46446f82 100644 --- a/gns3server/schemas/__init__.py +++ b/gns3server/schemas/__init__.py @@ -20,7 +20,7 @@ from .common import ErrorMessage from .version import Version # Controller schemas -from .controller.links import Link +from .controller.links import LinkCreate, LinkUpdate, Link from .controller.computes import ComputeCreate, ComputeUpdate, AutoIdlePC, Compute from .controller.templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template from .controller.drawings import Drawing diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 3a37ccbc..fbbf50a1 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -17,7 +17,7 @@ from pydantic import BaseModel, Field from typing import List, Optional from enum import Enum -from uuid import UUID +from uuid import UUID, uuid4 from .labels import Label @@ -42,24 +42,45 @@ class LinkType(str, Enum): serial = "serial" -class Link(BaseModel): +class LinkBase(BaseModel): """ Link data. """ - link_id: Optional[UUID] = None - project_id: Optional[UUID] = None - nodes: Optional[List[LinkNode]] = None + nodes: Optional[List[LinkNode]] = Field(None, min_items=0, max_items=2) suspend: Optional[bool] = None filters: Optional[dict] = None - capturing: Optional[bool] = Field(None, description="Read only property. True if a capture running on the link") + + +class LinkCreate(LinkBase): + + link_id: UUID = Field(default_factory=uuid4) + nodes: List[LinkNode] = Field(..., min_items=2, max_items=2) + + +class LinkUpdate(LinkBase): + + pass + + +class Link(LinkBase): + + link_id: UUID + project_id: Optional[UUID] = None + link_type: Optional[LinkType] = None + capturing: Optional[bool] = Field( + None, + description="Read only property. True if a capture running on the link" + ) capture_file_name: Optional[str] = Field( - None, description="Read only property. The name of the capture file if a capture is running" + None, + description="Read only property. The name of the capture file if a capture is running" ) capture_file_path: Optional[str] = Field( - None, description="Read only property. The full path of the capture file if a capture is running" + None, + description="Read only property. The full path of the capture file if a capture is running" ) capture_compute_id: Optional[str] = Field( - None, description="Read only property. The compute identifier where a capture is running" + None, + description="Read only property. The compute identifier where a capture is running" ) - link_type: Optional[LinkType] = None diff --git a/gns3server/utils/cpu_percent.py b/gns3server/utils/cpu_percent.py index 59463bb9..016c633b 100644 --- a/gns3server/utils/cpu_percent.py +++ b/gns3server/utils/cpu_percent.py @@ -20,7 +20,7 @@ import time class CpuPercent: """ - Ensures a minumum interval between two cpu_percent() calls + Ensures a minimum interval between two cpu_percent() calls """ _last_measurement = None # time of last measurement