Merge branch '2.1' into 2.2

# Conflicts:
#	gns3server/compute/builtin/nodes/cloud.py
#	gns3server/compute/docker/docker_vm.py
#	gns3server/compute/dynamips/nodes/ethernet_switch.py
#	gns3server/compute/iou/iou_vm.py
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/compute/virtualbox/virtualbox_vm.py
#	gns3server/compute/vmware/vmware_vm.py
#	gns3server/controller/__init__.py
#	gns3server/version.py
#	gns3server/web/web_server.py
This commit is contained in:
grossmj committed 2018-10-04 15:44:13 +02:00
commit 9de13c570c
36 files changed
+316 -93

No files matched your search

+14
View File
@@ -25,6 +25,13 @@
"kvm": "require"
},
"images": [
{
"filename": "nxosv-final.9.2.1.qcow2",
"version": "9.2.1",
"md5sum": "1d7fa4654602d7ffbf62544edfe71986",
"filesize": 1330315264,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.2%25281%2529"
},
{
"filename": "nxosv-final.7.0.3.I7.4.qcow2",
"version": "7.0.3.I7.4",
@@ -85,6 +92,13 @@
}
],
"versions": [
{
"name": "9.2.1",
"images": {
"bios_image": "OVMF-20160813.fd",
"hda_disk_image": "nxosv-final.9.2.1.qcow2"
}
},
{
"name": "7.0.3.I7.4",
"images": {
+14
View File
@@ -29,6 +29,13 @@
"options": "-smp 2 -cpu host"
},
"images": [
{
"filename": "BIG-IQ-5.4.0.2.24.7467.qcow2",
"version": "5.4.0.2",
"md5sum": "e3e6389438ba1e1676f507658f767e95",
"filesize": 3480748032,
"download_url": "https://downloads.f5.com/esd/serveDownload.jsp?path=/big-iq/big-iq_cm/5.4.0/english/virtual-edition_base-plus-hf2/&sw=BIG-IQ&pro=big-iq_CM&ver=5.4.0&container=Virtual-Edition_Base-Plus-HF2&file=BIG-IQ-5.4.0.2.24.7467.qcow2.zip"
},
{
"filename": "BIG-IQ-5.4.0.0.0.7437.qcow2",
"version": "5.4.0",
@@ -81,6 +88,13 @@
}
],
"versions": [
{
"name": "5.4.0.2",
"images": {
"hda_disk_image": "BIG-IQ-5.4.0.2.24.7467.qcow2",
"hdb_disk_image": "empty100G.qcow2"
}
},
{
"name": "5.4.0",
"images": {
+14 -1
View File
@@ -20,6 +20,14 @@
"kvm": "require"
},
"images": [
{
"filename": "kali-linux-2018.3-amd64.iso",
"version": "2018.3",
"md5sum": "6dc3e57177249f73492b9edb95d082d1",
"filesize": 3188391936,
"download_url": "https://www.kali.org/downloads/",
"direct_download_url": "http://cdimage.kali.org/kali-2018.3/kali-linux-2018.3-amd64.iso"
},
{
"filename": "kali-linux-2018.1-amd64.iso",
"version": "2018.1",
@@ -79,7 +87,12 @@
],
"versions": [
{
"name": "2018.8",
"name": "2018.3",
"images": {
"cdrom_image": "kali-linux-2018.3-amd64.iso"
}
}, {
"name": "2018.1",
"images": {
"cdrom_image": "kali-linux-2018.1-amd64.iso"
}
-1
View File
@@ -17,7 +17,6 @@
"ram": 2048,
"arch": "x86_64",
"console_type": "telnet",
"boot_priority": "dc",
"kvm": "allow",
"process_priority": "normal"
},
+43
View File
@@ -0,0 +1,43 @@
{
"name": "TacacsGUI",
"category": "guest",
"description": "TacacsGUI Free Access Control Server for Your Network Devices. GUI for powerful daemon. The project of Alexey Mochalin, based on tacacs daemon by Marc Huber",
"vendor_name": "TacacsGUI",
"vendor_url": "https://tacacsgui.com/",
"documentation_url": "https://tacacsgui.com/documentation/",
"product_name": "TacacsGUI",
"product_url": "https://drive.google.com/open?id=1U8tbj14NqEyCmarayhZm54qTyjgsJm4B",
"registry_version": 3,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "Credentials: SSH ---> username: root ---> password: 1234 MySQL DB: ---> username: root --> password: tacacs Web interface: ---> username: tacgui ---> password: abc123",
"port_name_format": "Port{port1}",
"qemu": {
"adapter_type": "e1000",
"adapters": 1,
"ram": 1024,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
"boot_priority": "c",
"kvm": "allow"
},
"images": [
{
"filename": "tac_plus.qcow2",
"version": "201710201114",
"md5sum": "6b5e66590051124dae586b8640b2eb11",
"filesize": 160301056,
"download_url": "https://drive.google.com/open?id=1U8tbj14NqEyCmarayhZm54qTyjgsJm4B"
}
],
"versions": [
{
"name": "201710201114",
"images": {
"hda_disk_image": "tac_plus.qcow2"
}
}
]
}
+1 -1
View File
@@ -259,7 +259,7 @@ class Cloud(BaseNode):
try:
output = yield from gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports")
except (FileNotFoundError, subprocess.SubprocessError) as e:
except (OSError, subprocess.SubprocessError) as e:
log.warning("Could not execute networksetup: {}".format(e))
return False
+66 -29
View File
@@ -30,6 +30,7 @@ from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer
from gns3server.utils.asyncio import wait_for_file_creation
from gns3server.utils.asyncio import asyncio_ensure_future
from gns3server.utils.asyncio import monitor_process
from gns3server.utils.get_resource import get_resource
from gns3server.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError
@@ -231,7 +232,10 @@ class DockerVM(BaseNode):
binds = ["{}:/gns3:ro".format(resources)]
# We mount our own etc/network
network_config = self._create_network_config()
try:
network_config = self._create_network_config()
except OSError as e:
raise DockerError("Could not create network config in the container: {}".format(e))
binds.append("{}:/gns3volumes/etc/network:rw".format(network_config))
self._volumes = ["/etc/network"]
@@ -290,11 +294,12 @@ class DockerVM(BaseNode):
try:
image_infos = yield from self._get_image_information()
except DockerHttp404Error:
log.info("Image %s is missing pulling it from docker hub", self._image)
log.info("Image '{}' is missing, pulling it from Docker hub...".format(self._image))
yield from self.pull_image(self._image)
image_infos = yield from self._get_image_information()
if image_infos is None:
raise DockerError("Can't get image informations, please try again.")
if image_infos is None:
raise DockerError("Cannot get information for image '{}', please try again.".format(self._image))
params = {
"Hostname": self._name,
@@ -318,7 +323,10 @@ class DockerVM(BaseNode):
if params["Entrypoint"] is None:
params["Entrypoint"] = []
if self._start_command:
params["Cmd"] = shlex.split(self._start_command)
try:
params["Cmd"] = shlex.split(self._start_command)
except ValueError as e:
raise DockerError("Invalid start command '{}': {}".format(self._start_command, e))
if len(params["Cmd"]) == 0:
params["Cmd"] = image_infos.get("Config", {"Cmd": []})["Cmd"]
if params["Cmd"] is None:
@@ -360,8 +368,7 @@ class DockerVM(BaseNode):
result = yield from self.manager.query("POST", "containers/create", data=params)
self._cid = result['Id']
log.info("Docker container '{name}' [{id}] created".format(
name=self._name, id=self._id))
log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id))
return True
def _format_env(self, variables, env):
@@ -457,16 +464,19 @@ class DockerVM(BaseNode):
@asyncio.coroutine
def _start_aux(self):
"""
Starts an auxiliary console
Start an auxiliary console
"""
# We can not use the API because docker doesn't expose a websocket api for exec
# https://github.com/GNS3/gns3-gui/issues/1039
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
stdin=asyncio.subprocess.PIPE)
try:
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
stdin=asyncio.subprocess.PIPE)
except OSError as e:
raise DockerError("Could not start auxiliary console process: {}".format(e))
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True)
try:
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
@@ -489,21 +499,25 @@ class DockerVM(BaseNode):
for volume in self._volumes:
log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format(
name=self._name, image=self._image, path=volume))
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker",
"exec",
self._cid,
"/gns3/bin/busybox",
"sh",
"-c",
"("
"/gns3/bin/busybox find \"{path}\" -depth -print0"
" | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\""
")"
" && /gns3/bin/busybox chmod -R u+rX \"{path}\""
" && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\""
.format(uid=os.getuid(), gid=os.getgid(), path=volume),
)
try:
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker",
"exec",
self._cid,
"/gns3/bin/busybox",
"sh",
"-c",
"("
"/gns3/bin/busybox find \"{path}\" -depth -print0"
" | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\""
")"
" && /gns3/bin/busybox chmod -R u+rX \"{path}\""
" && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\""
.format(uid=os.getuid(), gid=os.getgid(), path=volume),
)
except OSError as e:
raise DockerError("Could not fix permissions for {}: {}".format(volume, e))
yield from process.wait()
@asyncio.coroutine
@@ -514,7 +528,7 @@ class DockerVM(BaseNode):
self._display = self._get_free_display_port()
if shutil.which("Xvfb") is None or shutil.which("x11vnc") is None:
raise DockerError("Please install Xvfb and x11vnc before using the VNC support")
raise DockerError("Please install Xvfb and x11vnc before using VNC support")
self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16")
# We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569
self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
@@ -522,6 +536,29 @@ class DockerVM(BaseNode):
x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display))
yield from wait_for_file_creation(x11_socket)
#monitor_process(self._xvfb_process, self._xvfb_callback)
#monitor_process(self._x11vnc_process, self._x11vnc_callback)
def _xvfb_callback(self, returncode):
"""
Called when the process has stopped.
:param returncode: Process returncode
"""
if returncode != 0:
self.project.emit("log.error", {"message": "The Xvfb process has stopped, return code: {}.".format(returncode)})
def _x11vnc_callback(self, returncode):
"""
Called when the process has stopped.
:param returncode: Process returncode
"""
if returncode != 0:
self.project.emit("log.error", {"message": "The x11vnc process has stopped, return code: {}.".format(returncode)})
@asyncio.coroutine
def _start_http(self):
"""
@@ -87,6 +87,7 @@ class EthernetSwitch(Device):
self._mappings = {}
self._telnet_console = None
self._telnet_shell = None
self._telnet_server = None
self._console = console
self._console_type = console_type
@@ -233,8 +234,9 @@ class EthernetSwitch(Device):
"""
yield from self._telnet.close()
self._telnet_server.close()
if self._telnet_server:
self._telnet_server.close()
for nio in self._nios.values():
if nio:
yield from nio.close()
+2 -2
View File
@@ -369,7 +369,7 @@ class IOUVM(BaseNode):
try:
output = yield from gns3server.utils.asyncio.subprocess_check_output("ldd", self._path)
except (FileNotFoundError, subprocess.SubprocessError) as e:
except (OSError, subprocess.SubprocessError) as e:
log.warning("Could not determine the shared library dependencies for {}: {}".format(self._path, e))
return
@@ -422,7 +422,7 @@ class IOUVM(BaseNode):
hostid = (yield from gns3server.utils.asyncio.subprocess_check_output("hostid")).strip()
except FileNotFoundError as e:
raise IOUError("Could not find hostid: {}".format(e))
except subprocess.SubprocessError as e:
except (OSError, subprocess.SubprocessError) as e:
raise IOUError("Could not execute hostid: {}".format(e))
try:
+1 -1
View File
@@ -23,7 +23,7 @@ import logging
log = logging.getLogger(__name__)
# This ports are disallowed by Chrome and Firefox to avoid issues, we skip them as well
# These ports are disallowed by Chrome and Firefox to avoid issues, we skip them as well
BANNED_PORTS = set((1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103,
104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526,
530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667,
+2 -2
View File
@@ -194,7 +194,7 @@ class Qemu(BaseManager):
return version
else:
raise QemuError("Could not determine the Qemu version for {}".format(qemu_path))
except subprocess.SubprocessError as e:
except (OSError, subprocess.SubprocessError) as e:
raise QemuError("Error while looking for the Qemu version: {}".format(e))
@staticmethod
@@ -214,7 +214,7 @@ class Qemu(BaseManager):
return version
else:
raise QemuError("Could not determine the Qemu-img version for {}".format(qemu_img_path))
except subprocess.SubprocessError as e:
except (OSError, subprocess.SubprocessError) as e:
raise QemuError("Error while looking for the Qemu-img version: {}".format(e))
@staticmethod
+1 -1
View File
@@ -1101,7 +1101,7 @@ class QemuVM(BaseNode):
if expect in line:
result = line.decode("utf-8").strip()
break
except EOFError as e:
except (ConnectionError, EOFError) as e:
log.warning("Could not read from QEMU monitor: {}".format(e))
writer.close()
return result
@@ -217,6 +217,8 @@ class VirtualBoxVM(BaseNode):
except ET.ParseError:
raise VirtualBoxError("Cannot modify VirtualBox linked nodes file. "
"File {} is corrupted.".format(self._linked_vbox_file()))
except OSError as e:
raise VirtualBoxError("Cannot modify VirtualBox linked nodes file '{}': {}".format(self._linked_vbox_file(), e))
machine = tree.getroot().find("{http://www.virtualbox.org/}Machine")
if machine is not None and machine.get("uuid") != "{" + self.id + "}":
@@ -245,6 +247,7 @@ class VirtualBoxVM(BaseNode):
return True
return False
@locking
@asyncio.coroutine
def start(self):
"""
@@ -949,7 +952,11 @@ class VirtualBoxVM(BaseNode):
"""
if self.console and self.console_type == "telnet":
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
pipe_name = self._get_pipe_name()
try:
self._remote_pipe = yield from asyncio_open_serial(pipe_name)
except OSError as e:
raise VirtualBoxError("Could not open serial pipe '{}': {}".format(pipe_name, e))
server = AsyncioTelnetServer(reader=self._remote_pipe,
writer=self._remote_pipe,
binary=True,
+6
View File
@@ -150,6 +150,7 @@ class VMware(BaseManager):
VIX 1.13 was the release for Player 6.
VIX 1.14 was the release for Player 7.
VIX 1.15 was the release for Workstation Player 12.
VIX 1.17 was the release for Workstation Player 14.
:param player_version: VMware Player major version.
"""
@@ -163,6 +164,8 @@ class VMware(BaseManager):
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
elif player_version >= 12:
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
elif player_version >= 14:
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
self._host_type = "player"
@asyncio.coroutine
@@ -173,6 +176,7 @@ class VMware(BaseManager):
VIX 1.13 was the release for Workstation 10.
VIX 1.14 was the release for Workstation 11.
VIX 1.15 was the release for Workstation Pro 12.
VIX 1.17 was the release for Workstation Pro 14.
:param ws_version: VMware Workstation major version.
"""
@@ -186,6 +190,8 @@ class VMware(BaseManager):
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
elif ws_version >= 12:
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
elif ws_version >= 14:
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
self._host_type = "ws"
@asyncio.coroutine
+5 -1
View File
@@ -849,7 +849,11 @@ class VMwareVM(BaseNode):
"""
if self.console and self.console_type == "telnet":
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
pipe_name = self._get_pipe_name()
try:
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
except OSError as e:
raise VMwareError("Could not open serial pipe '{}': {}".format(pipe_name, e))
server = AsyncioTelnetServer(reader=self._remote_pipe,
writer=self._remote_pipe,
binary=True,
+1 -1
View File
@@ -316,7 +316,7 @@ class VPCSVM(BaseNode):
if sys.platform.startswith("win32"):
try:
self._process.send_signal(signal.CTRL_BREAK_EVENT)
except OSError:
except (SystemError, OSError):
pass
else:
try:
+6 -4
View File
@@ -117,7 +117,7 @@ class Controller:
self._appliance_templates = {}
for directory, builtin in ((get_resource('appliances'), True,), (self.appliances_path(), False,)):
if os.path.isdir(directory):
if directory and os.path.isdir(directory):
for file in os.listdir(directory):
if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'):
continue
@@ -208,8 +208,10 @@ class Controller:
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_hub"), {"node_type": "ethernet_hub", "name": "Ethernet hub", "category": 1, "symbol": ":/symbols/hub.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "frame_relay_switch"), {"node_type": "frame_relay_switch", "name": "Frame Relay switch", "category": 1, "symbol": ":/symbols/frame_relay_switch.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "atm_switch"), {"node_type": "atm_switch", "name": "ATM switch", "category": 1, "symbol": ":/symbols/atm_switch.svg"}, builtin=True))
if sys.platform.startswith("win"):
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "traceng"), {"node_type": "traceng", "name": "TraceNG", "default_name_format": "TraceNG-{0}", "category": 2, "symbol": ":/symbols/traceng.svg", "properties": {}}, builtin=True))
#FIXME: disable TraceNG
#if sys.platform.startswith("win"):
# builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "traceng"), {"node_type": "traceng", "name": "TraceNG", "default_name_format": "TraceNG-{0}", "category": 2, "symbol": ":/symbols/traceng.svg", "properties": {}}, builtin=True))
for b in builtins:
self._appliances[b.id] = b
@@ -250,7 +252,7 @@ class Controller:
for c in computes:
try:
yield from self.add_compute(**c)
except (aiohttp.web.HTTPConflict, KeyError):
except (aiohttp.web.HTTPError, KeyError):
pass # Skip not available servers at loading
yield from self.load_projects()
try:
+7 -3
View File
@@ -450,9 +450,13 @@ class Compute:
self._capabilities = response.json
if response.json["version"].split("-")[0] != __version__.split("-")[0]:
msg = "GNS3 controller version {} is not the same as compute server {} version {}".format(__version__,
self._name,
response.json["version"])
if self._name.startswith("GNS3 VM"):
msg = "GNS3 version {} is not the same as the GNS3 VM version {}. Please upgrade the GNS3 VM.".format(__version__,
response.json["version"])
else:
msg = "GNS3 controller version {} is not the same as compute server {} version {}".format(__version__,
self._name,
response.json["version"])
if __version_info__[3] == 0:
# Stable release
log.error(msg)
+4 -4
View File
@@ -94,12 +94,12 @@ class Node:
# Update node properties with additional elements
for prop in kwargs:
if prop not in ignore_properties:
if prop and prop not in ignore_properties:
if hasattr(self, prop):
try:
setattr(self, prop, kwargs[prop])
except AttributeError as e:
log.critical("Can't set attribute %s", prop)
log.critical("Cannot set attribute '%s'".format(prop))
raise e
else:
if prop not in self.CONTROLLER_ONLY_PROPERTIES and kwargs[prop] is not None and kwargs[prop] != "":
@@ -186,9 +186,9 @@ class Node:
if not os.path.isabs(path):
path = os.path.join(self.project.controller.configs_path(), path)
try:
with open(path) as f:
with open(path, encoding="utf-8") as f:
return f.read()
except (PermissionError, OSError):
except OSError:
return None
@property
+1 -1
View File
@@ -80,7 +80,7 @@ class StandardPortFactory:
segment_number,
adapter=adapter_number,
**cls._generate_replacement(interface_number, segment_number))
except (ValueError, KeyError) as e:
except (IndexError, ValueError, KeyError) as e:
raise aiohttp.web.HTTPConflict(text="Invalid port name format {}: {}".format(port_name_format, str(e)))
port_name = custom_adapter_settings.get("port_name", port_name)
+3 -3
View File
@@ -875,7 +875,7 @@ class Project:
link = yield from self.add_link(link_id=link_data["link_id"])
if "filters" in link_data:
yield from link.update_filters(link_data["filters"])
for node_link in link_data["nodes"]:
for node_link in link_data.get("nodes", []):
node = self.get_node(node_link["node_id"])
port = node.get_port(node_link["adapter_number"], node_link["port_number"])
if port is None:
@@ -903,7 +903,7 @@ class Project:
try:
if os.path.exists(path + ".backup"):
shutil.copy(path + ".backup", path)
except (PermissionError, OSError):
except OSError:
pass
self._status = "closed"
self._loading = False
@@ -968,7 +968,7 @@ class Project:
with open(project_path, "rb") as f:
project = yield from import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True)
except (ValueError, OSError, UnicodeEncodeError) as e:
raise aiohttp.web.HTTPConflict(text="Can not duplicate project: {}".format(str(e)))
raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e)))
if previous_status == "closed":
yield from self.close()
+14 -5
View File
@@ -22,6 +22,9 @@ from ..utils.get_resource import get_resource
from ..utils.picture import get_size
from ..config import Config
import logging
log = logging.getLogger(__name__)
class Symbols:
"""
@@ -73,19 +76,25 @@ class Symbols:
def symbols_path(self):
directory = os.path.expanduser(Config.instance().get_section_config("Server").get("symbols_path", "~/GNS3/symbols"))
if directory:
os.makedirs(directory, exist_ok=True)
try:
os.makedirs(directory, exist_ok=True)
except OSError as e:
log.error("Could not create symbol directory '{}': {}".format(directory, e))
return None
return directory
def get_path(self, symbol_id):
try:
return self._symbols_path[symbol_id]
# Symbol not found refresh cache
# Symbol not found, let's refresh the cache
except KeyError:
self.list()
try:
self.list()
return self._symbols_path[symbol_id]
except KeyError:
return self._symbols_path[":/symbols/computer.svg"]
except (OSError, KeyError):
log.warning("Could not retrieve symbol '{}'".format(symbol_id))
symbols_path = self._symbols_path
return symbols_path[":/symbols/computer.svg"]
def get_size(self, symbol_id):
try:
+4 -4
View File
@@ -436,7 +436,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 NotImplementedError("Conversion of {} is not supported".format(old_node["type"]))
raise aiohttp.web.HTTPConflict(text="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"]:
@@ -635,13 +635,13 @@ def _create_cloud(node, old_node, icon):
elif old_port["name"].startswith("nio_nat"):
continue
else:
raise NotImplementedError("The conversion of cloud with {} is not supported".format(old_port["name"]))
raise aiohttp.web.HTTPConflict(text="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 NotImplementedError("UDP tunnel using IPV6 is not supported in cloud")
raise aiohttp.web.HTTPConflict(text="UDP tunnel using IPV6 is not supported in cloud")
port = {
"name": "UDP tunnel {}".format(len(ports) + 1),
"port_number": len(ports) + 1,
@@ -672,7 +672,7 @@ def _convert_snapshots(topo_dir):
old_snapshots_dir = os.path.join(topo_dir, "project-files", "snapshots")
if os.path.exists(old_snapshots_dir):
new_snapshots_dir = os.path.join(topo_dir, "snapshots")
os.makedirs(new_snapshots_dir)
os.makedirs(new_snapshots_dir, exist_ok=True)
for snapshot in os.listdir(old_snapshots_dir):
snapshot_dir = os.path.join(old_snapshots_dir, snapshot)
+26 -4
View File
@@ -20,7 +20,7 @@ import sys
import struct
import aiohttp
import platform
import locale
try:
import raven
@@ -57,7 +57,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://56af21e241ed4c1894ebe17bf06b1cd1:6075f91067954267b51e90b9638a6fad@sentry.io/38482"
DSN = "https://8a4a7325dfcf4661a0b04d92b0a7d32e:14f83f7a65e54df88e5f06abad85b152@sentry.io/38482"
if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert):
@@ -92,7 +92,8 @@ class CrashReport:
"url": request.path,
"data": request.json,
})
self._client.tags_context({
context = {
"os:name": platform.system(),
"os:release": platform.release(),
"os:win_32": " ".join(platform.win32_ver()),
@@ -105,7 +106,28 @@ class CrashReport:
"python:bit": struct.calcsize("P") * 8,
"python:encoding": sys.getdefaultencoding(),
"python:frozen": "{}".format(hasattr(sys, "frozen"))
})
}
if sys.platform.startswith("linux") and not hasattr(sys, "frozen"):
# add locale information
try:
language, encoding = locale.getlocale()
context["locale:language"] = language
context["locale:encoding"] = encoding
except ValueError:
pass
# add GNS3 VM version if it exists
home = os.path.expanduser("~")
gns3vm_version = os.path.join(home, ".config", "GNS3", "gns3vm_version")
if os.path.isfile(gns3vm_version):
try:
with open(gns3vm_version) as fd:
context["gns3vm:version"] = fd.readline().strip()
except OSError:
pass
self._client.tags_context(context)
try:
report = self._client.captureException()
except Exception as e:
@@ -59,7 +59,7 @@ class ComputeHandler:
@Route.put(
r"/computes/{compute_id}",
description="Get a compute server information",
description="Update a compute server",
status_codes={
200: "Compute server updated",
400: "Invalid request",
@@ -302,9 +302,9 @@ class ProjectHandler:
try:
with tempfile.TemporaryDirectory() as tmp_dir:
datas = yield from export_project(
project, tmp_dir,
include_images=bool(int(request.query.get("include_images", "0"))))
stream = yield from export_project(project,
tmp_dir,
include_images=bool(int(request.query.get("include_images", "0"))))
# We need to do that now because export could failed and raise an HTTP error
# that why response start need to be the later possible
response.content_type = 'application/gns3project'
@@ -312,7 +312,7 @@ class ProjectHandler:
response.enable_chunked_encoding()
yield from response.prepare(request)
for data in datas:
for data in stream:
response.write(data)
yield from response.drain()
@@ -140,7 +140,7 @@ class ServerHandler:
r"/debug",
description="Dump debug information to disk (debug directory in config directory). Work only for local server",
status_codes={
201: "Writed"
201: "Written"
})
def debug(request, response):
@@ -157,7 +157,7 @@ class ServerHandler:
f.write(ServerHandler._getDebugData())
except Exception as e:
# If something is wrong we log the info to the log and we hope the log will be include correctly to the debug export
log.error("Could not export debug informations {}".format(e), exc_info=1)
log.error("Could not export debug information {}".format(e), exc_info=1)
try:
if Controller.instance().gns3vm.engine == "vmware":
@@ -52,7 +52,8 @@ class SymbolHandler:
controller = Controller.instance()
try:
yield from response.file(controller.symbols.get_path(request.match_info["symbol_id"]))
except (KeyError, FileNotFoundError, PermissionError):
except (KeyError, OSError) as e:
log.warning("Could not get symbol file: {}".format(e))
response.set_status(404)
@Route.post(
@@ -66,7 +67,7 @@ class SymbolHandler:
controller = Controller.instance()
path = os.path.join(controller.symbols.symbols_path(), os.path.basename(request.match_info["symbol_id"]))
try:
with open(path, 'wb') as f:
with open(path, "wb") as f:
while True:
try:
chunk = yield from request.content.read(1024)
@@ -75,7 +76,7 @@ class SymbolHandler:
if not chunk:
break
f.write(chunk)
except OSError as e:
except (UnicodeEncodeError, OSError) as e:
raise aiohttp.web.HTTPConflict(text="Could not write symbol file '{}': {}".format(path, e))
# Reset the symbol list
controller.symbols.list()
+1 -1
View File
@@ -178,7 +178,7 @@ class Hypervisor(UBridgeHypervisor):
env=env)
log.info("ubridge started PID={}".format(self._process.pid))
except (OSError, PermissionError, subprocess.SubprocessError) as e:
except (OSError, subprocess.SubprocessError) as e:
ubridge_stdout = self.read_stdout()
log.error("Could not start ubridge: {}\n{}".format(e, ubridge_stdout))
raise UbridgeError("Could not start ubridge: {}\n{}".format(e, ubridge_stdout))
+2 -4
View File
@@ -197,11 +197,9 @@ class AsyncioTelnetServer:
yield from self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws)
yield from connection.connected()
yield from self._process(network_reader, network_writer, connection)
except ConnectionResetError:
except ConnectionError:
with (yield from self._lock):
network_writer.close()
if self._reader_process == network_reader:
self._reader_process = None
# Cancel current read from this reader
@@ -217,7 +215,7 @@ class AsyncioTelnetServer:
try:
writer.write_eof()
yield from writer.drain()
except ConnectionResetError:
except (AttributeError, ConnectionError):
continue
@asyncio.coroutine
+10 -2
View File
@@ -43,8 +43,16 @@ class PingStats:
cur_time > cls._last_measurement + 1.9:
cls._last_measurement = cur_time
# Non blocking call to get cpu usage. First call will return 0
cls._last_cpu_percent = psutil.cpu_percent(interval=None)
cls._last_mem_percent = psutil.virtual_memory().percent
try:
cls._last_cpu_percent = psutil.cpu_percent(interval=None)
cls._last_mem_percent = psutil.virtual_memory().percent
except RuntimeError:
# ignore the following error:
# RuntimeError: host_statistics(HOST_CPU_LOAD_INFO) syscall failed: (ipc/send) invalid reply port
pass
except PermissionError:
# [Errno 13] Permission denied: '/proc/stat'
pass
stats["cpu_usage_percent"] = cls._last_cpu_percent
stats["memory_usage_percent"] = cls._last_mem_percent
return stats
+5
View File
@@ -225,6 +225,11 @@ class Route(object):
response = Response(request=request, route=route)
response.set_status(408)
response.json({"message": "Client error", "status": 408})
except MemoryError:
log.error("Memory error detected, server has run out of memory!", exc_info=1)
response = Response(request=request, route=route)
response.set_status(500)
response.json({"message": "Memory error", "status": 500})
except Exception as e:
log.error("Uncaught exception detected: {type}".format(type=type(e)), exc_info=1)
response = Response(request=request, route=route)
+6 -1
View File
@@ -29,7 +29,10 @@ import functools
import time
import atexit
from gns3server.utils.static import get_static_dir
# Import encoding now, to avoid implicit import later.
# Implicit import within threads may cause LookupError when standard library is in a ZIP
import encodings.idna
from .route import Route
from ..config import Config
from ..compute import MODULES
@@ -38,6 +41,8 @@ from ..compute.qemu import Qemu
from ..controller import Controller
from ..utils.asyncio import asyncio_ensure_future
from gns3server.utils.static import get_static_dir
# do not delete this import
import gns3server.handlers