Migration to FastAPI

This commit is contained in:
grossmj committed 2020-10-02 16:07:50 +09:30
1 parent c12b675691
commit eb3cb8a41f
224 files changed
+11254 -13517

No files matched your search

+18 -17
View File
@@ -21,7 +21,6 @@ import json
import uuid
import socket
import shutil
import aiohttp
from ..config import Config
from .project import Project
@@ -37,6 +36,7 @@ from .topology import load_topology
from .gns3vm import GNS3VM
from ..utils.get_resource import get_resource
from .gns3vm.gns3_vm_error import GNS3VMError
from .controller_error import ControllerError, ControllerNotFoundError
import logging
log = logging.getLogger(__name__)
@@ -90,14 +90,15 @@ class Controller:
port=port,
user=server_config.get("user", ""),
password=server_config.get("password", ""),
force=True)
except aiohttp.web.HTTPConflict:
force=True,
connect=True) # FIXME: not connection for now
except ControllerError:
log.fatal("Cannot access to the local server, make sure something else is not running on the TCP port {}".format(port))
sys.exit(1)
for c in computes:
try:
await self.add_compute(**c)
except (aiohttp.web.HTTPError, KeyError):
await self.add_compute(**c, connect=False) # FIXME: not connection for now
except (ControllerError, KeyError):
pass # Skip not available servers at loading
try:
@@ -127,7 +128,7 @@ class Controller:
try:
await compute.close()
# We don't care if a compute is down at this step
except (ComputeError, aiohttp.web.HTTPError, OSError):
except (ComputeError, ControllerError, OSError):
pass
await self.gns3vm.exit_vm()
#self.save()
@@ -150,9 +151,9 @@ class Controller:
try:
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
if not os.access(self._config_file, os.W_OK):
raise aiohttp.web.HTTPConflict(text="Change rejected, cannot write to controller configuration file '{}'".format(self._config_file))
raise ControllerNotFoundError("Change rejected, cannot write to controller configuration file '{}'".format(self._config_file))
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Change rejected: {}".format(e))
raise ControllerError("Change rejected: {}".format(e))
def save(self):
"""
@@ -240,7 +241,7 @@ class Controller:
if file.endswith(".gns3"):
try:
await self.load_project(os.path.join(project_dir, file), load=False)
except (aiohttp.web.HTTPConflict, aiohttp.web.HTTPNotFound, NotImplementedError):
except (ControllerError, NotImplementedError):
pass # Skip not compatible projects
except OSError as e:
log.error(str(e))
@@ -304,7 +305,7 @@ class Controller:
for compute in self._computes.values():
if name and compute.name == name and not force:
raise aiohttp.web.HTTPConflict(text='Compute name "{}" already exists'.format(name))
raise ControllerError('Compute name "{}" already exists'.format(name))
compute = Compute(compute_id=compute_id, controller=self, name=name, **kwargs)
self._computes[compute.id] = compute
@@ -349,7 +350,7 @@ class Controller:
try:
compute = self.get_compute(compute_id)
except aiohttp.web.HTTPNotFound:
except ControllerNotFoundError:
return
await self.close_compute_projects(compute)
await compute.close()
@@ -382,8 +383,8 @@ class Controller:
return self._computes[compute_id]
except KeyError:
if compute_id == "vm":
raise aiohttp.web.HTTPNotFound(text="Cannot use a node on the GNS3 VM server with the GNS3 VM not configured")
raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id))
raise ControllerNotFoundError("Cannot use a node on the GNS3 VM server with the GNS3 VM not configured")
raise ControllerNotFoundError("Compute ID {} doesn't exist".format(compute_id))
def has_compute(self, compute_id):
"""
@@ -405,9 +406,9 @@ class Controller:
for project in self._projects.values():
if name and project.name == name:
if path and path == project.path:
raise aiohttp.web.HTTPConflict(text='Project "{}" already exists in location "{}"'.format(name, path))
raise ControllerError('Project "{}" already exists in location "{}"'.format(name, path))
else:
raise aiohttp.web.HTTPConflict(text='Project "{}" already exists'.format(name))
raise ControllerError('Project "{}" already exists'.format(name))
project = Project(project_id=project_id, controller=self, name=name, path=path, **kwargs)
self._projects[project.id] = project
return self._projects[project.id]
@@ -421,7 +422,7 @@ class Controller:
try:
return self._projects[project_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
raise ControllerNotFoundError("Project ID {} doesn't exist".format(project_id))
async def get_loaded_project(self, project_id):
"""
@@ -488,7 +489,7 @@ class Controller:
break
i += 1
if i > 1000000:
raise aiohttp.web.HTTPConflict(text="A project name could not be allocated (node limit reached?)")
raise ControllerError("A project name could not be allocated (node limit reached?)")
return new_name
@property
+4 -3
View File
@@ -25,6 +25,7 @@ from .appliance import Appliance
from ..config import Config
from ..utils.asyncio import locking
from ..utils.get_resource import get_resource
from .controller_error import ControllerError
import logging
log = logging.getLogger(__name__)
@@ -173,7 +174,7 @@ class ApplianceManager:
log.info("Appliances are already up-to-date (ETag {})".format(self._appliances_etag))
return
elif response.status != 200:
raise aiohttp.web.HTTPConflict(text="Could not retrieve appliances from GitHub due to HTTP error code {}".format(response.status))
raise ControllerError("Could not retrieve appliances from GitHub due to HTTP error code {}".format(response.status))
etag = response.headers.get("ETag")
if etag:
self._appliances_etag = etag
@@ -200,9 +201,9 @@ class ApplianceManager:
with open(path, 'wb') as f:
f.write(appliance_data)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Could not write appliance file '{}': {}".format(path, e))
raise ControllerError("Could not write appliance file '{}': {}".format(path, e))
except ValueError as e:
raise aiohttp.web.HTTPConflict(text="Could not read appliances information from GitHub: {}".format(e))
raise ControllerError("Could not read appliances information from GitHub: {}".format(e))
# download the custom symbols
await self.download_custom_symbols()
+36 -31
View File
@@ -28,7 +28,12 @@ from operator import itemgetter
from ..utils import parse_version
from ..utils.asyncio import locking
from ..controller.controller_error import ControllerError
from ..controller.controller_error import (
ControllerError,
ControllerNotFoundError,
ControllerForbiddenError,
ControllerTimeoutError,
ControllerUnauthorizedError)
from ..version import __version__, __version_info__
@@ -40,7 +45,8 @@ class ComputeError(ControllerError):
pass
class ComputeConflict(aiohttp.web.HTTPConflict):
# FIXME: broken
class ComputeConflict(ComputeError):
"""
Raise when the compute send a 409 that we can handle
@@ -48,7 +54,7 @@ class ComputeConflict(aiohttp.web.HTTPConflict):
"""
def __init__(self, response):
super().__init__(text=response["message"])
super().__init__(response["message"])
self.response = response
@@ -78,15 +84,16 @@ class Compute:
self._closed = False # Close mean we are destroying the compute node
self._controller = controller
self._set_auth(user, password)
self._cpu_usage_percent = None
self._memory_usage_percent = None
self._disk_usage_percent = None
self._cpu_usage_percent = 0
self._memory_usage_percent = 0
self._disk_usage_percent = 0
self._last_error = None
self._capabilities = {
"version": None,
"cpus": None,
"memory": None,
"disk_size": None,
"version": "",
"platform": "",
"cpus": 0,
"memory": 0,
"disk_size": 0,
"node_types": []
}
self.name = name
@@ -317,7 +324,7 @@ class Compute:
url = self._getUrl("/projects/{}/files/{}".format(project.id, path))
response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404:
raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(path))
raise ControllerNotFoundError("{} not found on compute".format(path))
return response
async def download_image(self, image_type, image):
@@ -332,7 +339,7 @@ class Compute:
url = self._getUrl("/{}/images/{}".format(image_type, image))
response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404:
raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(image))
raise ControllerNotFoundError("{} not found on compute".format(image))
return response
async def http_query(self, method, path, data=None, dont_connect=False, **kwargs):
@@ -355,7 +362,7 @@ class Compute:
"""
try:
await self.connect()
except aiohttp.web.HTTPConflict:
except ControllerError:
pass
@locking
@@ -383,19 +390,19 @@ class Compute:
asyncio.get_event_loop().call_later(5, lambda: asyncio.ensure_future(self._try_reconnect()))
return
except aiohttp.web.HTTPNotFound:
raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server or it's a 1.X server".format(self._id))
raise ControllerNotFoundError("The server {} is not a GNS3 server or it's a 1.X server".format(self._id))
except aiohttp.web.HTTPUnauthorized:
raise aiohttp.web.HTTPConflict(text="Invalid auth for server {}".format(self._id))
raise ControllerUnauthorizedError("Invalid auth for server {}".format(self._id))
except aiohttp.web.HTTPServiceUnavailable:
raise aiohttp.web.HTTPConflict(text="The server {} is unavailable".format(self._id))
raise ControllerNotFoundError("The server {} is unavailable".format(self._id))
except ValueError:
raise aiohttp.web.HTTPConflict(text="Invalid server url for server {}".format(self._id))
raise ComputeError("Invalid server url for server {}".format(self._id))
if "version" not in response.json:
msg = "The server {} is not a GNS3 server".format(self._id)
log.error(msg)
await self._http_session.close()
raise aiohttp.web.HTTPConflict(text=msg)
raise ControllerNotFoundError(msg)
self._capabilities = response.json
if response.json["version"].split("-")[0] != __version__.split("-")[0]:
@@ -411,13 +418,13 @@ class Compute:
log.error(msg)
await self._http_session.close()
self._last_error = msg
raise aiohttp.web.HTTPConflict(text=msg)
raise ControllerError(msg)
elif parse_version(__version__)[:2] != parse_version(response.json["version"])[:2]:
# We don't allow different major version to interact even with dev build
log.error(msg)
await self._http_session.close()
self._last_error = msg
raise aiohttp.web.HTTPConflict(text=msg)
raise ControllerError(msg)
else:
msg = "{}\nUsing different versions may result in unexpected problems. Please use at your own risk.".format(msg)
self._controller.notification.controller_emit("log.warning", {"message": msg})
@@ -521,7 +528,7 @@ class Compute:
response = await self._session().request(method, url, headers=headers, data=data, auth=self._auth, chunked=chunked, timeout=timeout)
except asyncio.TimeoutError:
raise ComputeError("Timeout error for {} call to {} after {}s".format(method, url, timeout))
except (aiohttp.ClientError, aiohttp.ServerDisconnectedError, ValueError, KeyError, socket.gaierror) as e:
except (aiohttp.ClientError, aiohttp.ServerDisconnectedError, aiohttp.ClientResponseError, ValueError, KeyError, socket.gaierror) as e:
# aiohttp 2.3.1 raises socket.gaierror when cannot find host
raise ComputeError(str(e))
body = await response.read()
@@ -538,22 +545,20 @@ class Compute:
else:
msg = ""
if response.status == 400:
raise aiohttp.web.HTTPBadRequest(text="Bad request {} {}".format(url, body))
elif response.status == 401:
raise aiohttp.web.HTTPUnauthorized(text="Invalid authentication for compute {}".format(self.id))
if response.status == 401:
raise ControllerUnauthorizedError("Invalid authentication for compute {}".format(self.id))
elif response.status == 403:
raise aiohttp.web.HTTPForbidden(text=msg)
raise ControllerForbiddenError(msg)
elif response.status == 404:
raise aiohttp.web.HTTPNotFound(text="{} {} not found".format(method, path))
raise ControllerNotFoundError("{} {} not found".format(method, path))
elif response.status == 408 or response.status == 504:
raise aiohttp.web.HTTPRequestTimeout(text="{} {} request timeout".format(method, path))
raise ControllerTimeoutError("{} {} request timeout".format(method, path))
elif response.status == 409:
try:
raise ComputeConflict(json.loads(body))
# If the 409 doesn't come from a GNS3 server
except ValueError:
raise aiohttp.web.HTTPConflict(text=msg)
raise ControllerError(msg)
elif response.status == 500:
raise aiohttp.web.HTTPInternalServerError(text="Internal server error {}".format(url))
elif response.status == 503:
@@ -567,7 +572,7 @@ class Compute:
try:
response.json = json.loads(body)
except ValueError:
raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server".format(self._id))
raise ControllerError("The server {} is not a GNS3 server".format(self._id))
else:
response.json = {}
response.body = b""
@@ -585,7 +590,7 @@ class Compute:
return response
async def delete(self, path, **kwargs):
return (await self.http_query("DELETE", path, **kwargs))
return await self.http_query("DELETE", path, **kwargs)
async def forward(self, method, type, path, data=None):
"""
+26 -2
View File
@@ -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
@@ -18,7 +18,7 @@
class ControllerError(Exception):
def __init__(self, message):
def __init__(self, message: str):
super().__init__(message)
self._message = message
@@ -27,3 +27,27 @@ class ControllerError(Exception):
def __str__(self):
return self._message
class ControllerNotFoundError(ControllerError):
def __init__(self, message: str):
super().__init__(message)
class ControllerUnauthorizedError(ControllerError):
def __init__(self, message: str):
super().__init__(message)
class ControllerForbiddenError(ControllerError):
def __init__(self, message: str):
super().__init__(message)
class ControllerTimeoutError(ControllerError):
def __init__(self, message: str):
super().__init__(message)
+12 -11
View File
@@ -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
@@ -20,10 +20,11 @@ import sys
import json
import asyncio
import aiofiles
import aiohttp
import zipfile
import tempfile
from .controller_error import ControllerError, ControllerNotFoundError, ControllerTimeoutError
from datetime import datetime
import logging
@@ -51,13 +52,13 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
# To avoid issue with data not saved we disallow the export of a running project
if project.is_running():
raise aiohttp.web.HTTPConflict(text="Project must be stopped in order to export it")
raise ControllerError("Project must be stopped in order to export it")
# Make sure we save the project
project.dump()
if not os.path.exists(project._path):
raise aiohttp.web.HTTPNotFound(text="Project could not be found at '{}'".format(project._path))
raise ControllerNotFoundError("Project could not be found at '{}'".format(project._path))
# First we process the .gns3 in order to be sure we don't have an error
for file in os.listdir(project._path):
@@ -99,7 +100,7 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
try:
data = await response.content.read(CHUNK_SIZE)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading file '{}' from remote compute {}:{}".format(compute_file["path"], compute.host, compute.port))
raise ControllerTimeoutError("Timeout when downloading file '{}' from remote compute {}:{}".format(compute_file["path"], compute.host, compute.port))
if not data:
break
await f.write(data)
@@ -175,7 +176,7 @@ async def _patch_project_file(project, path, zstream, include_images, keep_compu
with open(path) as f:
topology = json.load(f)
except (OSError, ValueError) as e:
raise aiohttp.web.HTTPConflict(text="Project file '{}' cannot be read: {}".format(path, e))
raise ControllerError("Project file '{}' cannot be read: {}".format(path, e))
if "topology" in topology:
if "nodes" in topology["topology"]:
@@ -183,9 +184,9 @@ async def _patch_project_file(project, path, zstream, include_images, keep_compu
compute_id = node.get('compute_id', 'local')
if node["node_type"] == "virtualbox" and node.get("properties", {}).get("linked_clone"):
raise aiohttp.web.HTTPConflict(text="Projects with a linked {} clone node cannot not be exported. Please use Qemu instead.".format(node["node_type"]))
raise ControllerError("Projects with a linked {} clone node cannot not be exported. Please use Qemu instead.".format(node["node_type"]))
if not allow_all_nodes and node["node_type"] in ["virtualbox", "vmware"]:
raise aiohttp.web.HTTPConflict(text="Projects with a {} node cannot be exported".format(node["node_type"]))
raise ControllerError("Projects with a {} node cannot be exported".format(node["node_type"]))
if not keep_compute_id:
node["compute_id"] = "local" # To make project portable all node by default run on local
@@ -272,11 +273,11 @@ async def _export_remote_images(project, compute_id, image_type, image, project_
try:
compute = [compute for compute in project.computes if compute.id == compute_id][0]
except IndexError:
raise aiohttp.web.HTTPConflict(text="Cannot export image from '{}' compute. Compute doesn't exist.".format(compute_id))
raise ControllerNotFoundError("Cannot export image from '{}' compute. Compute doesn't exist.".format(compute_id))
response = await compute.download_image(image_type, image)
if response.status != 200:
raise aiohttp.web.HTTPConflict(text="Cannot export image from compute '{}'. Compute returned status code {}.".format(compute_id, response.status))
raise ControllerError("Cannot export image from compute '{}'. Compute returned status code {}.".format(compute_id, response.status))
(fd, temp_path) = tempfile.mkstemp(dir=temporary_dir)
async with aiofiles.open(fd, 'wb') as f:
@@ -284,7 +285,7 @@ async def _export_remote_images(project, compute_id, image_type, image, project_
try:
data = await response.content.read(CHUNK_SIZE)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading image '{}' from remote compute {}:{}".format(image, compute.host, compute.port))
raise ControllerTimeoutError("Timeout when downloading image '{}' from remote compute {}:{}".format(image, compute.host, compute.port))
if not data:
break
await f.write(data)
+4 -4
View File
@@ -18,7 +18,6 @@
import sys
import copy
import asyncio
import aiohttp
import ipaddress
from ...utils.asyncio import locking
@@ -29,6 +28,7 @@ from .remote_gns3_vm import RemoteGNS3VM
from .gns3_vm_error import GNS3VMError
from ...version import __version__
from ..compute import ComputeError
from ..controller_error import ControllerError
import logging
log = logging.getLogger(__name__)
@@ -285,7 +285,7 @@ class GNS3VM:
force=True)
compute.set_last_error(str(e))
except aiohttp.web.HTTPConflict:
except ControllerError:
pass
log.error("Cannot start the GNS3 VM: {}".format(e))
@@ -376,8 +376,8 @@ class GNS3VM:
self._controller.notification.controller_emit("log.warning", {"message": msg})
except ComputeError as e:
log.warning("Could not check the VM is in the same subnet as the local server: {}".format(e))
except aiohttp.web.HTTPConflict as e:
log.warning("Could not check the VM is in the same subnet as the local server: {}".format(e.text))
except ControllerError as e:
log.warning("Could not check the VM is in the same subnet as the local server: {}".format(e))
@locking
async def _suspend(self):
+12 -12
View File
@@ -21,11 +21,11 @@ import json
import uuid
import shutil
import zipfile
import aiohttp
import aiofiles
import itertools
import tempfile
from .controller_error import ControllerError
from .topology import load_topology
from ..utils.asyncio import wait_run_in_executor
from ..utils.asyncio import aiozipstream
@@ -55,15 +55,15 @@ async def import_project(controller, project_id, stream, location=None, name=Non
"""
if location and ".gns3" in location:
raise aiohttp.web.HTTPConflict(text="The destination path should not contain .gns3")
raise ControllerError("The destination path should not contain .gns3")
try:
with zipfile.ZipFile(stream) as zip_file:
project_file = zip_file.read("project.gns3").decode()
except zipfile.BadZipFile:
raise aiohttp.web.HTTPConflict(text="Cannot import project, not a GNS3 project (invalid zip)")
raise ControllerError("Cannot import project, not a GNS3 project (invalid zip)")
except KeyError:
raise aiohttp.web.HTTPConflict(text="Cannot import project, project.gns3 file could not be found")
raise ControllerError("Cannot import project, project.gns3 file could not be found")
try:
topology = json.loads(project_file)
@@ -77,7 +77,7 @@ async def import_project(controller, project_id, stream, location=None, name=Non
else:
project_name = controller.get_free_project_name(topology["name"])
except (ValueError, KeyError):
raise aiohttp.web.HTTPConflict(text="Cannot import project, the project.gns3 file is corrupted")
raise ControllerError("Cannot import project, the project.gns3 file is corrupted")
if location:
path = location
@@ -87,13 +87,13 @@ async def import_project(controller, project_id, stream, location=None, name=Non
try:
os.makedirs(path, exist_ok=True)
except UnicodeEncodeError:
raise aiohttp.web.HTTPConflict(text="The project name contain non supported or invalid characters")
raise ControllerError("The project name contain non supported or invalid characters")
try:
with zipfile.ZipFile(stream) as zip_file:
await wait_run_in_executor(zip_file.extractall, path)
except zipfile.BadZipFile:
raise aiohttp.web.HTTPConflict(text="Cannot extract files from GNS3 project (invalid zip)")
raise ControllerError("Cannot extract files from GNS3 project (invalid zip)")
topology = load_topology(os.path.join(path, "project.gns3"))
topology["name"] = project_name
@@ -257,9 +257,9 @@ async def _import_snapshots(snapshots_path, project_name, project_id):
with zipfile.ZipFile(f) as zip_file:
await wait_run_in_executor(zip_file.extractall, tmpdir)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot open snapshot '{}': {}".format(os.path.basename(snapshot), e))
raise ControllerError("Cannot open snapshot '{}': {}".format(os.path.basename(snapshot), e))
except zipfile.BadZipFile:
raise aiohttp.web.HTTPConflict(text="Cannot extract files from snapshot '{}': not a GNS3 project (invalid zip)".format(os.path.basename(snapshot)))
raise ControllerError("Cannot extract files from snapshot '{}': not a GNS3 project (invalid zip)".format(os.path.basename(snapshot)))
# patch the topology with the correct project name and ID
try:
@@ -272,9 +272,9 @@ async def _import_snapshots(snapshots_path, project_name, project_id):
with open(topology_file_path, "w+", encoding="utf-8") as f:
json.dump(topology, f, indent=4, sort_keys=True)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the project.gns3 file cannot be modified: {}".format(os.path.basename(snapshot), e))
raise ControllerError("Cannot update snapshot '{}': the project.gns3 file cannot be modified: {}".format(os.path.basename(snapshot), e))
except (ValueError, KeyError):
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the project.gns3 file is corrupted".format(os.path.basename(snapshot)))
raise ControllerError("Cannot update snapshot '{}': the project.gns3 file is corrupted".format(os.path.basename(snapshot)))
# write everything back to the original snapshot file
try:
@@ -287,4 +287,4 @@ async def _import_snapshots(snapshots_path, project_name, project_id):
async for chunk in zstream:
await f.write(chunk)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the snapshot cannot be recreated: {}".format(os.path.basename(snapshot), e))
raise ControllerError("Cannot update snapshot '{}': the snapshot cannot be recreated: {}".format(os.path.basename(snapshot), e))
+8 -7
View File
@@ -19,7 +19,8 @@ import os
import re
import uuid
import html
import aiohttp
from .controller_error import ControllerError, ControllerNotFoundError
import logging
log = logging.getLogger(__name__)
@@ -225,26 +226,26 @@ class Link:
port = node.get_port(adapter_number, port_number)
if port is None:
raise aiohttp.web.HTTPNotFound(text="Port {}/{} for {} not found".format(adapter_number, port_number, node.name))
raise ControllerNotFoundError("Port {}/{} for {} not found".format(adapter_number, port_number, node.name))
if port.link is not None:
raise aiohttp.web.HTTPConflict(text="Port is already used")
raise ControllerError("Port is already used")
self._link_type = port.link_type
for other_node in self._nodes:
if other_node["node"] == node:
raise aiohttp.web.HTTPConflict(text="Cannot connect to itself")
raise ControllerError("Cannot connect to itself")
if node.node_type in ["nat", "cloud"]:
if other_node["node"].node_type in ["nat", "cloud"]:
raise aiohttp.web.HTTPConflict(text="Connecting a {} to a {} is not allowed".format(other_node["node"].node_type, node.node_type))
raise ControllerError("Connecting a {} to a {} is not allowed".format(other_node["node"].node_type, node.node_type))
# Check if user is not connecting serial => ethernet
other_port = other_node["node"].get_port(other_node["adapter_number"], other_node["port_number"])
if other_port is None:
raise aiohttp.web.HTTPNotFound(text="Port {}/{} for {} not found".format(other_node["adapter_number"], other_node["port_number"], other_node["node"].name))
raise ControllerNotFoundError("Port {}/{} for {} not found".format(other_node["adapter_number"], other_node["port_number"], other_node["node"].name))
if port.link_type != other_port.link_type:
raise aiohttp.web.HTTPConflict(text="Connecting a {} interface to a {} interface is not allowed".format(other_port.link_type, port.link_type))
raise ControllerError("Connecting a {} interface to a {} interface is not allowed".format(other_port.link_type, port.link_type))
if label is None:
label = {
+13 -11
View File
@@ -15,7 +15,6 @@
# 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 aiohttp
import asyncio
import html
import copy
@@ -23,6 +22,7 @@ import uuid
import os
from .compute import ComputeConflict, ComputeError
from .controller_error import ControllerError, ControllerTimeoutError
from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory
from ..utils.images import images_directories
from ..utils.qt import qt_font_to_style
@@ -420,7 +420,7 @@ class Node:
compute_properties = kwargs[prop]
else:
if prop == "name" and self.status == "started" and self._node_type not in ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch"):
raise aiohttp.web.HTTPConflict(text="Sorry, it is not possible to rename a node that is already powered on")
raise ControllerError("Sorry, it is not possible to rename a node that is already powered on")
setattr(self, prop, kwargs[prop])
if compute_properties and "custom_adapters" in compute_properties:
@@ -532,7 +532,7 @@ class Node:
else:
await self.post("/start", data=data, timeout=240)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when starting {}".format(self._name))
raise ControllerTimeoutError("Timeout when starting {}".format(self._name))
async def stop(self):
"""
@@ -541,10 +541,10 @@ class Node:
try:
await self.post("/stop", timeout=240, dont_connect=True)
# We don't care if a node is down at this step
except (ComputeError, aiohttp.ClientError, aiohttp.web.HTTPError):
except (ComputeError, ControllerError):
pass
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when stopping {}".format(self._name))
raise ControllerTimeoutError("Timeout when stopping {}".format(self._name))
async def suspend(self):
"""
@@ -553,7 +553,7 @@ class Node:
try:
await self.post("/suspend", timeout=240)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name))
raise ControllerTimeoutError("Timeout when reloading {}".format(self._name))
async def reload(self):
"""
@@ -562,7 +562,7 @@ class Node:
try:
await self.post("/reload", timeout=240)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name))
raise ControllerTimeoutError("Timeout when reloading {}".format(self._name))
async def reset_console(self):
"""
@@ -573,7 +573,7 @@ class Node:
try:
await self.post("/console/reset", timeout=240)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reset console {}".format(self._name))
raise ControllerTimeoutError("Timeout when reset console {}".format(self._name))
async def post(self, path, data=None, **kwargs):
"""
@@ -602,15 +602,17 @@ class Node:
HTTP post on the node
"""
if path is None:
return (await self._compute.delete("/projects/{}/{}/nodes/{}".format(self._project.id, self._node_type, self._id), **kwargs))
return await self._compute.delete("/projects/{}/{}/nodes/{}".format(self._project.id, self._node_type, self._id), **kwargs)
else:
return (await self._compute.delete("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs))
return await self._compute.delete("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs)
async def _upload_missing_image(self, type, img):
"""
Search an image on local computer and upload it to remote compute
if the image exists
"""
print("UPLOAD MISSING IMAGE")
for directory in images_directories(type):
image = os.path.join(directory, img)
if os.path.exists(image):
@@ -619,7 +621,7 @@ class Node:
with open(image, 'rb') as f:
await self._compute.post("/{}/images/{}".format(self._node_type, os.path.basename(img)), data=f, timeout=None)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Can't upload {}: {}".format(image, str(e)))
raise ControllerError("Can't upload {}: {}".format(image, str(e)))
self.project.emit_notification("log.info", {"message": "Upload finished for {}".format(img)})
return True
return False
+2 -3
View File
@@ -16,10 +16,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import aiohttp
from contextlib import contextmanager
from ..notification_queue import NotificationQueue
from .controller_error import ControllerError
class Notification:
@@ -106,9 +106,8 @@ class Notification:
project = self._controller.get_project(event["project_id"])
node = project.get_node(event["node_id"])
await node.parse_node_response(event)
self.project_emit("node.updated", node.__json__())
except (aiohttp.web.HTTPNotFound, aiohttp.web.HTTPForbidden): # Project closing
except ControllerError: # Project closing
return
elif action == "ping":
event["compute_id"] = compute_id
+3 -3
View File
@@ -15,8 +15,7 @@
# 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 aiohttp
from gns3server.controller.controller_error import ControllerError
from gns3server.utils import macaddress_to_int, int_to_macaddress
from .atm_port import ATMPort
from .frame_relay_port import FrameRelayPort
@@ -26,6 +25,7 @@ from .ethernet_port import EthernetPort
from .serial_port import SerialPort
from .pos_port import POSPort
import logging
log = logging.getLogger(__name__)
@@ -81,7 +81,7 @@ class StandardPortFactory:
adapter=adapter_number,
**cls._generate_replacement(interface_number, segment_number))
except (IndexError, ValueError, KeyError) as e:
raise aiohttp.web.HTTPConflict(text="Invalid port name format {}: {}".format(port_name_format, str(e)))
raise ControllerError("Invalid port name format {}: {}".format(port_name_format, str(e)))
port_name = custom_adapter_settings.get("port_name", port_name)
port = PortFactory(port_name, segment_number, adapter_number, port_number, "ethernet")
+34 -34
View File
@@ -23,7 +23,6 @@ import copy
import shutil
import time
import asyncio
import aiohttp
import aiofiles
import tempfile
import zipfile
@@ -44,6 +43,7 @@ from ..utils.asyncio import locking
from ..utils.asyncio import aiozipstream
from .export_project import export_project
from .import_project import import_project
from .controller_error import ControllerError, ControllerForbiddenError, ControllerNotFoundError
import logging
log = logging.getLogger(__name__)
@@ -56,7 +56,7 @@ def open_required(func):
def wrapper(self, *args, **kwargs):
if self._status == "closed":
raise aiohttp.web.HTTPForbidden(text="The project is not opened")
raise ControllerForbiddenError("The project is not opened")
return func(self, *args, **kwargs)
return wrapper
@@ -100,7 +100,7 @@ class Project:
# Disallow overwrite of existing project
if project_id is None and path is not None:
if os.path.exists(path):
raise aiohttp.web.HTTPForbidden(text="The path {} already exist.".format(path))
raise ControllerForbiddenError("The path {} already exist.".format(path))
if project_id is None:
self._id = str(uuid4())
@@ -108,7 +108,7 @@ class Project:
try:
UUID(project_id, version=4)
except ValueError:
raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(project_id))
raise ControllerError("{} is not a valid UUID".format(project_id))
self._id = project_id
if path is None:
@@ -404,10 +404,10 @@ class Project:
try:
os.makedirs(path, exist_ok=True)
except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
raise ControllerError("Could not create project directory: {}".format(e))
if '"' in path:
raise aiohttp.web.HTTPForbidden(text="You are not allowed to use \" in the project directory path. Not supported by Dynamips.")
raise ControllerForbiddenError("You are not allowed to use \" in the project directory path. Not supported by Dynamips.")
self._path = path
@@ -472,9 +472,9 @@ class Project:
try:
name = base_name.format(number, id=number, name="Node")
except KeyError as e:
raise aiohttp.web.HTTPConflict(text="{" + e.args[0] + "} is not a valid replacement string in the node name")
raise ControllerError("{" + e.args[0] + "} is not a valid replacement string in the node name")
except (ValueError, IndexError) as e:
raise aiohttp.web.HTTPConflict(text="{} is not a valid replacement string in the node name".format(base_name))
raise ControllerError("{} is not a valid replacement string in the node name".format(base_name))
if name not in self._allocated_node_names:
self._allocated_node_names.add(name)
return name
@@ -488,7 +488,7 @@ class Project:
if name not in self._allocated_node_names:
self._allocated_node_names.add(name)
return name
raise aiohttp.web.HTTPConflict(text="A node name could not be allocated (node limit reached?)")
raise ControllerError("A node name could not be allocated (node limit reached?)")
def update_node_name(self, node, new_name):
@@ -507,7 +507,7 @@ class Project:
except KeyError:
msg = "Template {} doesn't exist".format(template_id)
log.error(msg)
raise aiohttp.web.HTTPNotFound(text=msg)
raise ControllerNotFoundError(msg)
template["x"] = x
template["y"] = y
node_type = template.pop("template_type")
@@ -599,7 +599,7 @@ class Project:
async def delete_node(self, node_id):
node = self.get_node(node_id)
if node.locked:
raise aiohttp.web.HTTPConflict(text="Node {} cannot be deleted because it is locked".format(node.name))
raise ControllerError("Node {} cannot be deleted because it is locked".format(node.name))
await self.__delete_node_links(node)
self.remove_allocated_node_name(node.name)
del self._nodes[node.id]
@@ -615,7 +615,7 @@ class Project:
try:
return self._nodes[node_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Node ID {} doesn't exist".format(node_id))
raise ControllerNotFoundError("Node ID {} doesn't exist".format(node_id))
def _get_closed_data(self, section, id_key):
"""
@@ -631,7 +631,7 @@ class Project:
with open(path, "r") as f:
topology = json.load(f)
except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not load topology: {}".format(e))
raise ControllerError("Could not load topology: {}".format(e))
try:
data = {}
@@ -639,7 +639,7 @@ class Project:
data[elem[id_key]] = elem
return data
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Section {} not found in the topology".format(section))
raise ControllerNotFoundError("Section {} not found in the topology".format(section))
@property
def nodes(self):
@@ -684,13 +684,13 @@ class Project:
try:
return self._drawings[drawing_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Drawing ID {} doesn't exist".format(drawing_id))
raise ControllerNotFoundError("Drawing ID {} doesn't exist".format(drawing_id))
@open_required
async def delete_drawing(self, drawing_id):
drawing = self.get_drawing(drawing_id)
if drawing.locked:
raise aiohttp.web.HTTPConflict(text="Drawing ID {} cannot be deleted because it is locked".format(drawing_id))
raise ControllerError("Drawing ID {} cannot be deleted because it is locked".format(drawing_id))
del self._drawings[drawing.id]
self.dump()
self.emit_notification("drawing.deleted", drawing.__json__())
@@ -730,7 +730,7 @@ class Project:
try:
return self._links[link_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Link ID {} doesn't exist".format(link_id))
raise ControllerNotFoundError("Link ID {} doesn't exist".format(link_id))
@property
def links(self):
@@ -756,7 +756,7 @@ class Project:
try:
return self._snapshots[snapshot_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Snapshot ID {} doesn't exist".format(snapshot_id))
raise ControllerNotFoundError("Snapshot ID {} doesn't exist".format(snapshot_id))
@open_required
async def snapshot(self, name):
@@ -767,7 +767,7 @@ class Project:
"""
if name in [snap.name for snap in self._snapshots.values()]:
raise aiohttp.web.HTTPConflict(text="The snapshot name {} already exists".format(name))
raise ControllerError("The snapshot name {} already exists".format(name))
snapshot = Snapshot(self, name=name)
await snapshot.create()
self._snapshots[snapshot.id] = snapshot
@@ -792,7 +792,7 @@ class Project:
try:
await compute.post("/projects/{}/close".format(self._id), dont_connect=True)
# We don't care if a compute is down at this step
except (ComputeError, aiohttp.web.HTTPError, aiohttp.ClientResponseError, TimeoutError):
except (ComputeError, ControllerError, TimeoutError):
pass
self._clean_pictures()
self._status = "closed"
@@ -839,18 +839,18 @@ class Project:
if self._status != "opened":
try:
await self.open()
except aiohttp.web.HTTPConflict as e:
except ControllerError as e:
# ignore missing images or other conflicts when deleting a project
log.warning("Conflict while deleting project: {}".format(e.text))
log.warning("Conflict while deleting project: {}".format(e))
await self.delete_on_computes()
await self.close()
try:
project_directory = get_default_project_directory()
if not os.path.commonprefix([project_directory, self.path]) == project_directory:
raise aiohttp.web.HTTPConflict(text="Project '{}' cannot be deleted because it is not in the default project directory: '{}'".format(self._name, project_directory))
raise ControllerError("Project '{}' cannot be deleted because it is not in the default project directory: '{}'".format(self._name, project_directory))
shutil.rmtree(self.path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e)))
raise ControllerError("Cannot delete project directory {}: {}".format(self.path, str(e)))
async def delete_on_computes(self):
"""
@@ -874,7 +874,7 @@ class Project:
try:
os.makedirs(path, exist_ok=True)
except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
raise ControllerError("Could not create project directory: {}".format(e))
return path
def _topology_file(self):
@@ -887,7 +887,7 @@ class Project:
"""
if self._closing is True:
raise aiohttp.web.HTTPConflict(text="Project is closing, please try again in a few seconds...")
raise ControllerError("Project is closing, please try again in a few seconds...")
if self._status == "opened":
return
@@ -966,7 +966,7 @@ class Project:
try:
await compute.post("/projects/{}/close".format(self._id))
# We don't care if a compute is down at this step
except (ComputeError, aiohttp.web.HTTPNotFound, aiohttp.web.HTTPConflict, aiohttp.ServerDisconnectedError):
except ComputeError:
pass
try:
if os.path.exists(path + ".backup"):
@@ -976,7 +976,7 @@ class Project:
self._status = "closed"
self._loading = False
if isinstance(e, ComputeError):
raise aiohttp.web.HTTPConflict(text=str(e))
raise ControllerError(str(e))
else:
raise e
try:
@@ -1047,7 +1047,7 @@ class Project:
log.info("Project '{}' duplicated in {:.4f} seconds".format(project.name, time.time() - begin))
except (ValueError, OSError, UnicodeEncodeError) as e:
raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e)))
raise ControllerError("Cannot duplicate project: {}".format(str(e)))
if previous_status == "closed":
await self.close()
@@ -1076,7 +1076,7 @@ class Project:
json.dump(topo, f, indent=4, sort_keys=True)
shutil.move(path + ".tmp", path)
except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not write topology: {}".format(e))
raise ControllerError("Could not write topology: {}".format(e))
@open_required
async def start_all(self):
@@ -1131,7 +1131,7 @@ class Project:
:returns: New node
"""
if node.status != "stopped" and not node.is_always_running():
raise aiohttp.web.HTTPConflict(text="Cannot duplicate node data while the node is running")
raise ControllerError("Cannot duplicate node data while the node is running")
data = copy.deepcopy(node.__json__(topology_dump=True))
# Some properties like internal ID should not be duplicated
@@ -1161,10 +1161,10 @@ class Project:
await node.post("/duplicate", timeout=None, data={
"destination_node_id": new_node_uuid
})
except aiohttp.web.HTTPNotFound as e:
except ControllerNotFoundError:
await self.delete_node(new_node_uuid)
raise aiohttp.web.HTTPConflict(text="This node type cannot be duplicated")
except aiohttp.web.HTTPConflict as e:
raise ControllerError("This node type cannot be duplicated")
except ControllerError as e:
await self.delete_node(new_node_uuid)
raise e
return new_node
+5 -5
View File
@@ -23,9 +23,9 @@ import tempfile
import aiofiles
import zipfile
import time
import aiohttp.web
from datetime import datetime, timezone
from .controller_error import ControllerError
from ..utils.asyncio import wait_run_in_executor
from ..utils.asyncio import aiozipstream
from .export_project import export_project
@@ -85,13 +85,13 @@ class Snapshot:
"""
if os.path.exists(self.path):
raise aiohttp.web.HTTPConflict(text="The snapshot file '{}' already exists".format(self.name))
raise ControllerError("The snapshot file '{}' already exists".format(self.name))
snapshot_directory = os.path.join(self._project.path, "snapshots")
try:
os.makedirs(snapshot_directory, exist_ok=True)
except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not create the snapshot directory '{}': {}".format(snapshot_directory, e))
raise ControllerError("Could not create the snapshot directory '{}': {}".format(snapshot_directory, e))
try:
begin = time.time()
@@ -104,7 +104,7 @@ class Snapshot:
await f.write(chunk)
log.info("Snapshot '{}' created in {:.4f} seconds".format(self.name, time.time() - begin))
except (ValueError, OSError, RuntimeError) as e:
raise aiohttp.web.HTTPConflict(text="Could not create snapshot file '{}': {}".format(self.path, e))
raise ControllerError("Could not create snapshot file '{}': {}".format(self.path, e))
async def restore(self):
"""
@@ -123,7 +123,7 @@ class Snapshot:
with open(self._path, "rb") as f:
project = await import_project(self._project.controller, self._project.id, f, location=self._project.path)
except (OSError, PermissionError) as e:
raise aiohttp.web.HTTPConflict(text=str(e))
raise ControllerError(str(e))
await project.open()
self._project.emit_notification("snapshot.restored", self.__json__())
return self._project
+3 -3
View File
@@ -16,10 +16,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import aiohttp
import posixpath
from .symbol_themes import BUILTIN_SYMBOL_THEMES
from .controller_error import ControllerNotFoundError
from ..utils.get_resource import get_resource
from ..utils.picture import get_size
from ..config import Config
@@ -54,7 +54,7 @@ class Symbols:
def theme(self, theme):
if not self._themes.get(theme):
raise aiohttp.web.HTTPNotFound(text="Could not find symbol theme '{}'".format(theme))
raise ControllerNotFoundError("Could not find symbol theme '{}'".format(theme))
self._current_theme = theme
def default_symbols(self):
@@ -65,7 +65,7 @@ class Symbols:
theme = self._themes.get(symbol_theme, None)
if not theme:
raise aiohttp.web.HTTPNotFound(text="Could not find symbol theme '{}'".format(symbol_theme))
raise ControllerNotFoundError("Could not find symbol theme '{}'".format(symbol_theme))
symbol_path = theme.get(symbol)
if symbol_path not in self._symbols_path:
log.warning("Default symbol {} was not found".format(symbol_path))
+6 -6
View File
@@ -17,9 +17,9 @@
import copy
import uuid
import aiohttp
import jsonschema
from .controller_error import ControllerError, ControllerNotFoundError
from .template import Template
import logging
@@ -85,14 +85,14 @@ class TemplateManager:
template_id = settings.get("template_id", "")
if template_id in self._templates:
raise aiohttp.web.HTTPConflict(text="Template ID '{}' already exists".format(template_id))
raise ControllerError("Template ID '{}' already exists".format(template_id))
else:
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)
raise aiohttp.web.HTTPBadRequest(text=message)
raise ControllerError(message)
from . import Controller
Controller.instance().check_can_write_config()
@@ -112,7 +112,7 @@ class TemplateManager:
template = self._templates.get(template_id)
if not template:
raise aiohttp.web.HTTPNotFound(text="Template ID {} doesn't exist".format(template_id))
raise ControllerNotFoundError("Template ID {} doesn't exist".format(template_id))
return template
def delete_template(self, template_id):
@@ -124,7 +124,7 @@ class TemplateManager:
template = self.get_template(template_id)
if template.builtin:
raise aiohttp.web.HTTPConflict(text="Template ID {} cannot be deleted because it is a builtin".format(template_id))
raise ControllerError("Template ID {} cannot be deleted because it is a builtin".format(template_id))
from . import Controller
Controller.instance().check_can_write_config()
self._templates.pop(template_id)
@@ -140,7 +140,7 @@ class TemplateManager:
template = self.get_template(template_id)
if template.builtin:
raise aiohttp.web.HTTPConflict(text="Template ID {} cannot be duplicated because it is a builtin".format(template_id))
raise ControllerError("Template ID {} cannot be duplicated because it is a builtin".format(template_id))
template_settings = copy.deepcopy(template.settings)
del template_settings["template_id"]
return self.add_template(template_settings)
+11 -11
View File
@@ -23,7 +23,6 @@ import uuid
import glob
import shutil
import zipfile
import aiohttp
import jsonschema
@@ -32,6 +31,7 @@ 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
import logging
log = logging.getLogger(__name__)
@@ -64,7 +64,7 @@ def _check_topology_schema(topo):
e.message,
json.dumps(e.schema))
log.critical(error)
raise aiohttp.web.HTTPConflict(text=error)
raise ControllerError(error)
def project_to_topology(project):
@@ -134,10 +134,10 @@ def load_topology(path):
with open(path, encoding="utf-8") as f:
topo = json.load(f)
except (OSError, UnicodeDecodeError, ValueError) as e:
raise aiohttp.web.HTTPConflict(text="Could not load topology {}: {}".format(path, str(e)))
raise ControllerError("Could not load topology {}: {}".format(path, str(e)))
if topo.get("revision", 0) > GNS3_FILE_FORMAT_REVISION:
raise aiohttp.web.HTTPConflict(text="This project was created with more recent version of GNS3 (file revision: {}). Please upgrade GNS3 to version {} or later".format(topo["revision"], topo["version"]))
raise ControllerError("This project was created with more recent version of GNS3 (file revision: {}). Please upgrade GNS3 to version {} or later".format(topo["revision"], topo["version"]))
changed = False
if "revision" not in topo or topo["revision"] < GNS3_FILE_FORMAT_REVISION:
@@ -145,7 +145,7 @@ def load_topology(path):
try:
shutil.copy(path, path + ".backup{}".format(topo.get("revision", 0)))
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Can't write backup of the topology {}: {}".format(path, str(e)))
raise ControllerError("Can't write backup of the topology {}: {}".format(path, str(e)))
changed = True
# update the version because we converted the topology
topo["version"] = __version__
@@ -187,7 +187,7 @@ def load_topology(path):
try:
_check_topology_schema(topo)
except aiohttp.web.HTTPConflict as e:
except ControllerError as e:
log.error("Can't load the topology %s", path)
raise e
@@ -196,7 +196,7 @@ def load_topology(path):
with open(path, "w+", encoding="utf-8") as f:
json.dump(topo, f, indent=4, sort_keys=True)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Can't write the topology {}: {}".format(path, str(e)))
raise ControllerError("Can't write the topology {}: {}".format(path, str(e)))
return topo
@@ -284,7 +284,7 @@ def _convert_2_0_0_beta_2(topo, topo_path):
for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "configs", "i{}_*".format(dynamips_id))):
shutil.move(path, os.path.join(node_dir, "configs", os.path.basename(path)))
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Can't convert project {}: {}".format(topo_path, str(e)))
raise ControllerError("Can't convert project {}: {}".format(topo_path, str(e)))
return topo
@@ -472,7 +472,7 @@ def _convert_1_3_later(topo, topo_path):
symbol = old_node.get("symbol", ":/symbols/computer.svg")
old_node["ports"] = _create_cloud(node, old_node, symbol)
else:
raise aiohttp.web.HTTPConflict(text="Conversion of {} is not supported".format(old_node["type"]))
raise ControllerError("Conversion of {} is not supported".format(old_node["type"]))
for prop in old_node.get("properties", {}):
if prop not in ["console", "name", "console_type", "console_host", "use_ubridge"]:
@@ -671,13 +671,13 @@ def _create_cloud(node, old_node, icon):
elif old_port["name"].startswith("nio_nat"):
continue
else:
raise aiohttp.web.HTTPConflict(text="The conversion of cloud with {} is not supported".format(old_port["name"]))
raise ControllerError("The conversion of cloud with {} is not supported".format(old_port["name"]))
if port_type == "udp":
try:
_, lport, rhost, rport = old_port["name"].split(":")
except ValueError:
raise aiohttp.web.HTTPConflict(text="UDP tunnel using IPV6 is not supported in cloud")
raise ControllerError("UDP tunnel using IPV6 is not supported in cloud")
port = {
"name": "UDP tunnel {}".format(len(ports) + 1),
"port_number": len(ports) + 1,
+5 -7
View File
@@ -16,9 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import aiohttp
from .controller_error import ControllerError, ControllerNotFoundError
from .link import Link
@@ -52,7 +50,7 @@ class UDPLink(Link):
try:
(node1_host, node2_host) = await node1.compute.get_ip_on_same_subnet(node2.compute)
except ValueError as e:
raise aiohttp.web.HTTPConflict(text="Cannot get an IP address on same subnet: {}".format(e))
raise ControllerError("Cannot get an IP address on same subnet: {}".format(e))
# Reserve a UDP port on both side
response = await node1.compute.post("/projects/{}/ports/udp".format(self._project.id))
@@ -142,7 +140,7 @@ class UDPLink(Link):
try:
await node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), timeout=120)
# If the node is already deleted (user selected multiple element and delete all in the same time)
except aiohttp.web.HTTPNotFound:
except ControllerNotFoundError:
pass
try:
@@ -154,7 +152,7 @@ class UDPLink(Link):
try:
await node2.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), timeout=120)
# If the node is already deleted (user selected multiple element and delete all in the same time)
except aiohttp.web.HTTPNotFound:
except ControllerNotFoundError:
pass
await super().delete()
@@ -216,7 +214,7 @@ class UDPLink(Link):
if node["node"].node_type and node["node"].status == "started":
return node
raise aiohttp.web.HTTPConflict(text="Cannot capture because there is no running device on this link")
raise ControllerError("Cannot capture because there is no running device on this link")
async def node_updated(self, node):
"""