Some cleaning.

This commit is contained in:
grossmj 2021-04-17 18:36:32 +09:30
parent bad3ef7003
commit 44074ff7c9
22 changed files with 122 additions and 99 deletions

View File

@ -71,7 +71,7 @@ async def get_links(project_id: UUID):
409: {"model": schemas.ErrorMessage, "description": "Could not create link"}, 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. 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) @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. Update a link.
""" """

View File

@ -40,7 +40,12 @@ class EthernetHub(BaseNode):
def __json__(self): 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): async def create(self):
""" """

View File

@ -40,7 +40,12 @@ class EthernetSwitch(BaseNode):
def __json__(self): 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): async def create(self):
""" """

View File

@ -67,4 +67,7 @@ class NIOGenericEthernet(NIO):
def __json__(self): def __json__(self):
return {"type": "nio_generic_ethernet", "ethernet_device": self._ethernet_device} return {
"type": "nio_generic_ethernet",
"ethernet_device": self._ethernet_device
}

View File

@ -66,4 +66,7 @@ class NIOLinuxEthernet(NIO):
def __json__(self): def __json__(self):
return {"type": "nio_linux_ethernet", "ethernet_device": self._ethernet_device} return {
"type": "nio_linux_ethernet",
"ethernet_device": self._ethernet_device
}

View File

@ -60,4 +60,7 @@ class NIOTAP(NIO):
def __json__(self): def __json__(self):
return {"type": "nio_tap", "tap_device": self._tap_device} return {
"type": "nio_tap",
"tap_device": self._tap_device
}

View File

@ -128,4 +128,9 @@ class NIOUDP(NIO):
def __json__(self): 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
}

View File

@ -81,4 +81,8 @@ class NIOUNIX(NIO):
def __json__(self): 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
}

View File

@ -81,4 +81,8 @@ class NIOVDE(NIO):
def __json__(self): 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
}

View File

@ -50,4 +50,7 @@ class NIOEthernet(NIO):
def __json__(self): def __json__(self):
return {"type": "nio_ethernet", "ethernet_device": self._ethernet_device} return {
"type": "nio_ethernet",
"ethernet_device": self._ethernet_device
}

View File

@ -50,4 +50,7 @@ class NIOTAP(NIO):
def __json__(self): def __json__(self):
return {"type": "nio_tap", "tap_device": self._tap_device} return {
"type": "nio_tap",
"tap_device": self._tap_device
}

View File

@ -74,4 +74,9 @@ class NIOUDP(NIO):
def __json__(self): 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
}

View File

@ -79,7 +79,11 @@ class Project:
def __json__(self): 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): def is_local(self):

View File

@ -17,14 +17,15 @@
import copy import copy
import uuid import uuid
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Appliance: class Appliance:
def __init__(self, appliance_id, data, builtin=True): def __init__(self, appliance_id, data, builtin=True):
if appliance_id is None: if appliance_id is None:
self._id = str(uuid.uuid4()) self._id = str(uuid.uuid4())
elif isinstance(appliance_id, uuid.UUID): elif isinstance(appliance_id, uuid.UUID):

View File

@ -390,7 +390,6 @@ class Node:
data["node_id"] = self._id data["node_id"] = self._id
if self._node_type == "docker": if self._node_type == "docker":
timeout = None timeout = None
else: else:
timeout = 1200 timeout = 1200
trial = 0 trial = 0
@ -543,7 +542,7 @@ class Node:
if self.custom_adapters: if self.custom_adapters:
data["custom_adapters"] = 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()): for key in list(data.keys()):
if data[key] is None or data[key] is {} or key in self.CONTROLLER_ONLY_PROPERTIES: if data[key] is None or data[key] is {} or key in self.CONTROLLER_ONLY_PROPERTIES:
del data[key] del data[key]
@ -628,7 +627,7 @@ class Node:
async def put(self, path, data=None, **kwargs): async def put(self, path, data=None, **kwargs):
""" """
HTTP post on the node HTTP put on the node
""" """
if path is None: if path is None:
path = f"/projects/{self._project.id}/{self._node_type}/nodes/{self._id}" path = f"/projects/{self._project.id}/{self._node_type}/nodes/{self._id}"
@ -780,11 +779,10 @@ class Node:
def __json__(self, topology_dump=False): 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: topology = {
return {
"compute_id": str(self._compute.id), "compute_id": str(self._compute.id),
"node_id": self._id, "node_id": self._id,
"node_type": self._node_type, "node_type": self._node_type,
@ -808,35 +806,18 @@ class Node:
"port_segment_size": self._port_segment_size, "port_segment_size": self._port_segment_size,
"first_port_name": self._first_port_name, "first_port_name": self._first_port_name,
"custom_adapters": self._custom_adapters, "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

View File

@ -28,6 +28,7 @@ class Notification:
""" """
def __init__(self, controller): def __init__(self, controller):
self._controller = controller self._controller = controller
self._project_listeners = {} self._project_listeners = {}
self._controller_listeners = set() self._controller_listeners = set()
@ -71,19 +72,6 @@ class Notification:
:param event: Event to send :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: for controller_listener in self._controller_listeners:
controller_listener.put_nowait((action, event, {})) controller_listener.put_nowait((action, event, {}))
@ -127,19 +115,6 @@ class Notification:
:param event: Event to send :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: if "project_id" in event or project_id:
self._send_event_to_project(event.get("project_id", project_id), action, event) self._send_event_to_project(event.get("project_id", project_id), action, event)
else: else:

View File

@ -633,7 +633,7 @@ class Project:
def _get_closed_data(self, section, id_key): def _get_closed_data(self, section, id_key):
""" """
Get the data for a project from the .gns3 when 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 section: The section name in the .gns3
:param id_key: The key for the element unique id :param id_key: The key for the element unique id
@ -1117,7 +1117,7 @@ class Project:
try: try:
topo = project_to_topology(self) topo = project_to_topology(self)
path = self._topology_file() 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: with open(path + ".tmp", "w+", encoding="utf-8") as f:
json.dump(topo, f, indent=4, sort_keys=True) json.dump(topo, f, indent=4, sort_keys=True)
shutil.move(path + ".tmp", path) shutil.move(path + ".tmp", path)
@ -1176,6 +1176,7 @@ class Project:
:param z: Z position :param z: Z position
:returns: New node :returns: New node
""" """
if node.status != "stopped" and not node.is_always_running(): if node.status != "stopped" and not node.is_always_running():
raise ControllerError("Cannot duplicate node data while the node is running") raise ControllerError("Cannot duplicate node data while the node is running")

View File

@ -36,10 +36,6 @@ import logging
log = logging.getLogger(__name__) 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: class Snapshot:
""" """
A snapshot object A snapshot object
@ -59,7 +55,7 @@ class Snapshot:
filename = ( filename = (
self._name 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" + ".gns3project"
) )
else: else:
@ -67,7 +63,7 @@ class Snapshot:
datestring = filename.replace(self._name + "_", "").split(".")[0] datestring = filename.replace(self._name + "_", "").split(".")[0]
try: try:
self._created_at = ( 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: except ValueError:
self._created_at = datetime.utcnow().timestamp() self._created_at = datetime.utcnow().timestamp()

View File

@ -121,7 +121,8 @@ def load_topology(path):
""" """
Open a topology file, patch it for last GNS3 release and return it 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: try:
with open(path, encoding="utf-8") as f: with open(path, encoding="utf-8") as f:
topo = json.load(f) topo = json.load(f)

View File

@ -20,7 +20,7 @@ from .common import ErrorMessage
from .version import Version from .version import Version
# Controller schemas # Controller schemas
from .controller.links import Link from .controller.links import LinkCreate, LinkUpdate, Link
from .controller.computes import ComputeCreate, ComputeUpdate, AutoIdlePC, Compute from .controller.computes import ComputeCreate, ComputeUpdate, AutoIdlePC, Compute
from .controller.templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template from .controller.templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template
from .controller.drawings import Drawing from .controller.drawings import Drawing

View File

@ -17,7 +17,7 @@
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from typing import List, Optional from typing import List, Optional
from enum import Enum from enum import Enum
from uuid import UUID from uuid import UUID, uuid4
from .labels import Label from .labels import Label
@ -42,24 +42,45 @@ class LinkType(str, Enum):
serial = "serial" serial = "serial"
class Link(BaseModel): class LinkBase(BaseModel):
""" """
Link data. Link data.
""" """
link_id: Optional[UUID] = None nodes: Optional[List[LinkNode]] = Field(None, min_items=0, max_items=2)
project_id: Optional[UUID] = None
nodes: Optional[List[LinkNode]] = None
suspend: Optional[bool] = None suspend: Optional[bool] = None
filters: Optional[dict] = 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( 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( 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( 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

View File

@ -20,7 +20,7 @@ import time
class CpuPercent: 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 _last_measurement = None # time of last measurement