mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-13 02:15:52 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bccdfc97d1 | ||
|
|
792c4310eb | ||
|
|
3e21f96bf9 | ||
|
|
5f932fee9f | ||
|
|
ef38ccfefa | ||
|
|
670b6d689a | ||
|
|
fc75a6b8d3 | ||
|
|
647ac87c89 | ||
|
|
817a053dfc | ||
|
|
df4e4bec24 | ||
|
|
60ac6d2dfe | ||
|
|
c0a0a13bdd | ||
|
|
a3044ede77 | ||
|
|
df3baffd9b | ||
|
|
e7b8309a80 | ||
|
|
029344da14 | ||
|
|
d0bab9947e | ||
|
|
e4a6db8ebc | ||
|
|
181a31be32 | ||
|
|
8d368cd0f6 | ||
|
|
41d4ecc4b7 | ||
|
|
4d95e0b51f | ||
|
|
4efdefaf5a |
No files matched your search
@@ -1,5 +1,21 @@
|
||||
# Change Log
|
||||
|
||||
## 2.1.12 23/01/2019
|
||||
|
||||
* Tune how to get the size of SVG images. Ref https://github.com/GNS3/gns3-gui/issues/2674.
|
||||
* Automatically create a symbolic link to the IOU image in the IOU working directory. Fixes #1484
|
||||
* Fix link pause/filters only work for the first interface of Docker containers. Fixes #1482
|
||||
* Telnet console resize support for Docker VM.
|
||||
* Fix _fix_permissions() garbles permissions in Docker VM. Ref #1428
|
||||
* Fix "None is not of type 'integer'" when opening project containing a Qemu VM. Fixes #2610.
|
||||
* Only require Xtigervnc or Xvfb+x11vnc for Docker with vnc console. Ref #1438
|
||||
* Support tigervnc in Docker VM. Ref #1438
|
||||
* Update minimum VIX version requirements for VMware. Ref #1415.
|
||||
|
||||
## 2.1.11 28/09/2018
|
||||
|
||||
* Catch some exceptions.
|
||||
|
||||
## 2.1.10 15/09/2018
|
||||
|
||||
* Include locale information and GNS3 VM version in crash reports.
|
||||
@@ -677,7 +693,7 @@
|
||||
* Fix naming of IOU serial interfaces
|
||||
* Improve timeout management
|
||||
* When exporting debug information export GNS3 VM vmx content
|
||||
* /debug for exporting debug informations
|
||||
* /debug for exporting debug information
|
||||
* Raise error if using a non linked clone VM twice
|
||||
* Fix a possible deadlock at exit
|
||||
* Fix import of some old dynamips topologies
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
GET /v2/compute/debug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Return debug informations about the compute
|
||||
Return debug information about the compute
|
||||
|
||||
Response status codes
|
||||
**********************
|
||||
|
||||
@@ -24,6 +24,7 @@ import shutil
|
||||
import psutil
|
||||
import shlex
|
||||
import aiohttp
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
||||
@@ -82,12 +83,14 @@ class DockerVM(BaseNode):
|
||||
self._ethernet_adapters = []
|
||||
self._temporary_directory = None
|
||||
self._telnet_servers = []
|
||||
self._x11vnc_process = None
|
||||
self._vnc_process = None
|
||||
self._xvfb_process = None
|
||||
self._console_resolution = console_resolution
|
||||
self._console_http_path = console_http_path
|
||||
self._console_http_port = console_http_port
|
||||
self._console_websocket = None
|
||||
self._extra_hosts = extra_hosts
|
||||
self._permissions_fixed = False
|
||||
|
||||
self._volumes = []
|
||||
# Keep a list of created bridge
|
||||
@@ -439,12 +442,14 @@ class DockerVM(BaseNode):
|
||||
|
||||
if self.console_type == "telnet":
|
||||
yield from self._start_console()
|
||||
|
||||
elif self.console_type == "http" or self.console_type == "https":
|
||||
yield from self._start_http()
|
||||
|
||||
if self.allocate_aux:
|
||||
yield from self._start_aux()
|
||||
|
||||
self._permissions_fixed = False
|
||||
self.status = "started"
|
||||
log.info("Docker container '{name}' [{image}] started listen for {console_type} on {console}".format(name=self._name,
|
||||
image=self._image,
|
||||
@@ -460,11 +465,10 @@ class DockerVM(BaseNode):
|
||||
# We can not use the API because docker doesn't expose a websocket api for exec
|
||||
# https://github.com/GNS3/gns3-gui/issues/1039
|
||||
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)
|
||||
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)
|
||||
@@ -472,7 +476,7 @@ class DockerVM(BaseNode):
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
|
||||
except OSError as e:
|
||||
raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.aux, e))
|
||||
log.debug("Docker container '%s' started listen for auxilary telnet on %d", self.name, self.aux)
|
||||
log.debug("Docker container '%s' started listen for auxiliary telnet on %d", self.name, self.aux)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _fix_permissions(self):
|
||||
@@ -482,6 +486,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
log.info("Docker container '{name}' fix ownership, state = {state}".format(name=self._name, state=state))
|
||||
if state == "stopped" or state == "exited":
|
||||
# We need to restart it to fix permissions
|
||||
yield from self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
@@ -509,6 +514,7 @@ class DockerVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise DockerError("Could not fix permissions for {}: {}".format(volume, e))
|
||||
yield from process.wait()
|
||||
self._permissions_fixed = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_vnc(self):
|
||||
@@ -517,19 +523,46 @@ 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 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)
|
||||
if not (shutil.which("Xtigervnc") or shutil.which("Xvfb") and shutil.which("x11vnc")):
|
||||
raise DockerError("Please install tigervnc-standalone-server (recommended) or Xvfb + x11vnc before using VNC support")
|
||||
|
||||
if shutil.which("Xtigervnc"):
|
||||
with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd:
|
||||
self._vnc_process = yield from asyncio.create_subprocess_exec("Xtigervnc",
|
||||
"-geometry", self._console_resolution,
|
||||
"-depth", "16",
|
||||
"-interface", self._manager.port_manager.console_host,
|
||||
"-rfbport", str(self.console),
|
||||
"-AlwaysShared",
|
||||
"-SecurityTypes", "None",
|
||||
":{}".format(self._display),
|
||||
stdout=fd, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
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
|
||||
with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd:
|
||||
self._vnc_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,
|
||||
stdout=fd, stderr=subprocess.STDOUT)
|
||||
|
||||
x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display))
|
||||
yield from wait_for_file_creation(x11_socket)
|
||||
#monitor_process(self._vnc_process, self._vnc_callback)
|
||||
|
||||
#monitor_process(self._xvfb_process, self._xvfb_callback)
|
||||
#monitor_process(self._x11vnc_process, self._x11vnc_callback)
|
||||
|
||||
def _xvfb_callback(self, returncode):
|
||||
def _vnc_callback(self, returncode):
|
||||
"""
|
||||
Called when the process has stopped.
|
||||
|
||||
@@ -537,17 +570,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
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)})
|
||||
self.project.emit("log.error", {"message": "The vnc process has stopped, return code: {}.".format(returncode)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_http(self):
|
||||
@@ -570,6 +593,19 @@ class DockerVM(BaseNode):
|
||||
])
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _window_size_changed_callback(self, columns, rows):
|
||||
"""
|
||||
Called when the console window size has been changed.
|
||||
(when naws is enabled in the Telnet server)
|
||||
|
||||
:param columns: number of columns
|
||||
:param rows: number of rows
|
||||
"""
|
||||
|
||||
# resize the container TTY.
|
||||
yield from self._manager.query("POST", "containers/{}/resize?h={}&w={}".format(self._cid, rows, columns))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
"""
|
||||
@@ -592,8 +628,7 @@ class DockerVM(BaseNode):
|
||||
|
||||
output_stream = asyncio.StreamReader()
|
||||
input_stream = InputStream()
|
||||
|
||||
telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream, echo=True)
|
||||
telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream, echo=True, naws=True, window_size_changed_callback=self._window_size_changed_callback)
|
||||
try:
|
||||
self._telnet_servers.append((yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console)))
|
||||
except OSError as e:
|
||||
@@ -603,7 +638,6 @@ class DockerVM(BaseNode):
|
||||
input_stream.ws = self._console_websocket
|
||||
|
||||
output_stream.feed_data(self.name.encode() + b" console is now available... Press RETURN to get started.\r\n")
|
||||
|
||||
asyncio_ensure_future(self._read_console_output(self._console_websocket, output_stream))
|
||||
|
||||
@asyncio.coroutine
|
||||
@@ -678,14 +712,15 @@ class DockerVM(BaseNode):
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
|
||||
yield from self._fix_permissions()
|
||||
if not self._permissions_fixed:
|
||||
yield from self._fix_permissions()
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
if state != "stopped" or state != "exited":
|
||||
# t=5 number of seconds to wait before killing the container
|
||||
try:
|
||||
yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
|
||||
log.info("Docker container '{name}' [{image}] stopped".format(
|
||||
name=self._name, image=self._image))
|
||||
log.info("Docker container '{name}' [{image}] stopped".format(name=self._name, image=self._image))
|
||||
except DockerHttp304Error:
|
||||
# Container is already stopped
|
||||
pass
|
||||
@@ -725,12 +760,13 @@ class DockerVM(BaseNode):
|
||||
if state == "paused" or state == "running":
|
||||
yield from self.stop()
|
||||
if self.console_type == "vnc":
|
||||
if self._x11vnc_process:
|
||||
if self._vnc_process:
|
||||
try:
|
||||
self._x11vnc_process.terminate()
|
||||
yield from self._x11vnc_process.wait()
|
||||
self._vnc_process.terminate()
|
||||
yield from self._vnc_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
if self._xvfb_process:
|
||||
try:
|
||||
self._xvfb_process.terminate()
|
||||
yield from self._xvfb_process.wait()
|
||||
|
||||
@@ -517,6 +517,16 @@ class IOUVM(BaseNode):
|
||||
|
||||
if "IOURC" not in os.environ:
|
||||
env["IOURC"] = iourc_path
|
||||
|
||||
# create a symbolic link to the image to avoid IOU error "failed code signing checks"
|
||||
# on newer images, see https://github.com/GNS3/gns3-server/issues/1484
|
||||
try:
|
||||
symlink = os.path.join(self.working_dir, os.path.basename(self.path))
|
||||
if not os.path.islink(symlink):
|
||||
os.symlink(self.path, symlink)
|
||||
except OSError as e:
|
||||
raise IOUError("Could not create symbolic link: {}".format(e))
|
||||
|
||||
command = yield from self._build_command()
|
||||
try:
|
||||
log.info("Starting IOU: {}".format(command))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -149,6 +149,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.
|
||||
"""
|
||||
@@ -162,6 +163,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
|
||||
@@ -172,6 +175,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.
|
||||
"""
|
||||
@@ -185,6 +189,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
|
||||
|
||||
@@ -315,7 +315,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:
|
||||
|
||||
@@ -446,9 +446,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)
|
||||
|
||||
@@ -91,12 +91,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] != "":
|
||||
@@ -254,7 +254,7 @@ class Node:
|
||||
try:
|
||||
self._width, self._height, filetype = self._project.controller.symbols.get_size(val)
|
||||
except (ValueError, OSError) as e:
|
||||
log.error("Could not write symbol: {}".format(e))
|
||||
log.error("Could not set symbol: {}".format(e))
|
||||
# If symbol is invalid we replace it by the default
|
||||
self.symbol = ":/symbols/computer.svg"
|
||||
if self._label is None:
|
||||
|
||||
@@ -935,7 +935,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()
|
||||
|
||||
@@ -57,7 +57,7 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "https://71472e2845b2422cab117bdfa8af55ec:da50c8f08b9f44d8b905356741c278df@sentry.io/38482"
|
||||
DSN = "https://edb72a54588b4d04afc2de56b8cb5b24:7830885c8a2a4d0cb311de10ddfe3d27@sentry.io/38482"
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert = get_resource("cacert.pem")
|
||||
if cacert is not None and os.path.isfile(cacert):
|
||||
|
||||
@@ -263,10 +263,11 @@ class DockerHandler:
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = container.ethernet_adapters[int(request.match_info["adapter_number"])].get_nio(0)
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
nio = container.ethernet_adapters[adapter_number].get_nio(0)
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from container.adapter_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
yield from container.adapter_update_nio_binding(adapter_number, nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
|
||||
@@ -301,12 +301,9 @@ class IOUHandler:
|
||||
nio = vm.adapters[adapter_number].get_nio(port_number)
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.adapter_update_nio_binding(
|
||||
adapter_number,
|
||||
port_number,
|
||||
nio)
|
||||
yield from vm.adapter_update_nio_binding(adapter_number, port_number, nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
response.json(request.json)
|
||||
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
|
||||
@@ -42,9 +42,9 @@ class ServerHandler:
|
||||
|
||||
@Route.get(
|
||||
r"/debug",
|
||||
description="Return debug informations about the compute",
|
||||
description="Return debug information about the compute",
|
||||
status_codes={
|
||||
201: "Writed"
|
||||
201: "Written"
|
||||
})
|
||||
def debug(request, response):
|
||||
response.content_type = "text/plain"
|
||||
|
||||
@@ -279,12 +279,13 @@ class VMwareHandler:
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = vm.ethernet_adapters[int(request.match_info["adapter_number"])]
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
nio = vm.ethernet_adapters[adapter_number]
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
yield from vm.adapter_update_nio_binding(adapter_number, nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -287,9 +287,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'
|
||||
@@ -297,7 +297,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()
|
||||
|
||||
|
||||
@@ -139,7 +139,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):
|
||||
|
||||
@@ -156,7 +156,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":
|
||||
|
||||
@@ -60,7 +60,7 @@ class SymbolHandler:
|
||||
r"/symbols/{symbol_id:.+}/raw",
|
||||
description="Write the symbol file",
|
||||
status_codes={
|
||||
200: "Symbol returned"
|
||||
200: "Symbol written"
|
||||
},
|
||||
raw=True)
|
||||
def upload(request, response):
|
||||
@@ -78,6 +78,7 @@ class SymbolHandler:
|
||||
f.write(chunk)
|
||||
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()
|
||||
response.set_status(204)
|
||||
@@ -537,7 +537,7 @@ QEMU_OBJECT_SCHEMA = {
|
||||
"description": "Console TCP port",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"type": "integer"
|
||||
"type": ["integer", "null"]
|
||||
},
|
||||
"console_type": {
|
||||
"description": "Console type",
|
||||
|
||||
@@ -15,7 +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 re
|
||||
|
||||
import asyncio
|
||||
import asyncio.subprocess
|
||||
import struct
|
||||
@@ -62,10 +62,11 @@ READ_SIZE = 1024
|
||||
|
||||
class TelnetConnection(object):
|
||||
"""Default implementation of telnet connection which may but may not be used."""
|
||||
def __init__(self, reader, writer):
|
||||
def __init__(self, reader, writer, window_size_changed_callback=None):
|
||||
self.is_closing = False
|
||||
self._reader = reader
|
||||
self._writer = writer
|
||||
self._window_size_changed_callback = window_size_changed_callback
|
||||
|
||||
@property
|
||||
def reader(self):
|
||||
@@ -85,10 +86,13 @@ class TelnetConnection(object):
|
||||
"""Method called when client is disconnecting"""
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def window_size_changed(self, columns, rows):
|
||||
"""Method called when window size changed, only can occur when
|
||||
`naws` flag is enable in server configuration."""
|
||||
pass
|
||||
|
||||
if self._window_size_changed_callback:
|
||||
yield from self._window_size_changed_callback(columns, rows)
|
||||
|
||||
@asyncio.coroutine
|
||||
def feed(self, data):
|
||||
@@ -116,7 +120,7 @@ class TelnetConnection(object):
|
||||
class AsyncioTelnetServer:
|
||||
MAX_NEGOTIATION_READ = 10
|
||||
|
||||
def __init__(self, reader=None, writer=None, binary=True, echo=False, naws=False, connection_factory=None):
|
||||
def __init__(self, reader=None, writer=None, binary=True, echo=False, naws=False, window_size_changed_callback=None, connection_factory=None):
|
||||
"""
|
||||
Initializes telnet server
|
||||
:param naws when True make a window size negotiation
|
||||
@@ -131,6 +135,7 @@ class AsyncioTelnetServer:
|
||||
self._lock = asyncio.Lock()
|
||||
self._reader_process = None
|
||||
self._current_read = None
|
||||
self._window_size_changed_callback = window_size_changed_callback
|
||||
|
||||
self._binary = binary
|
||||
# If echo is true when the client send data
|
||||
@@ -139,8 +144,8 @@ class AsyncioTelnetServer:
|
||||
self._echo = echo
|
||||
self._naws = naws
|
||||
|
||||
def default_connection_factory(reader, writer):
|
||||
return TelnetConnection(reader, writer)
|
||||
def default_connection_factory(reader, writer, window_size_changed_callback):
|
||||
return TelnetConnection(reader, writer, window_size_changed_callback)
|
||||
|
||||
if connection_factory is None:
|
||||
connection_factory = default_connection_factory
|
||||
@@ -190,7 +195,7 @@ class AsyncioTelnetServer:
|
||||
@asyncio.coroutine
|
||||
def run(self, network_reader, network_writer):
|
||||
# Keep track of connected clients
|
||||
connection = self._connection_factory(network_reader, network_writer)
|
||||
connection = self._connection_factory(network_reader, network_writer, self._window_size_changed_callback)
|
||||
self._connections[network_writer] = connection
|
||||
|
||||
try:
|
||||
@@ -215,7 +220,7 @@ class AsyncioTelnetServer:
|
||||
try:
|
||||
writer.write_eof()
|
||||
yield from writer.drain()
|
||||
except ConnectionError:
|
||||
except (AttributeError, ConnectionError):
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
@@ -307,6 +312,7 @@ class AsyncioTelnetServer:
|
||||
cmd.append(buffer[location])
|
||||
return op
|
||||
|
||||
@asyncio.coroutine
|
||||
def _negotiate(self, data, connection):
|
||||
""" Performs negotiation commands"""
|
||||
|
||||
@@ -314,7 +320,7 @@ class AsyncioTelnetServer:
|
||||
if command == NAWS:
|
||||
if len(payload) == 4:
|
||||
columns, rows = struct.unpack(str('!HH'), bytes(payload))
|
||||
connection.window_size_changed(columns, rows)
|
||||
yield from connection.window_size_changed(columns, rows)
|
||||
else:
|
||||
log.warning('Wrong number of NAWS bytes')
|
||||
else:
|
||||
@@ -373,7 +379,7 @@ class AsyncioTelnetServer:
|
||||
break
|
||||
|
||||
# SE command is followed by IAC, remove the last two operations from stack
|
||||
self._negotiate(negotiation[0:-2], connection)
|
||||
yield from self._negotiate(negotiation[0:-2], connection)
|
||||
|
||||
# This must be a 3-byte TELNET command
|
||||
else:
|
||||
|
||||
@@ -15,6 +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 re
|
||||
import io
|
||||
import struct
|
||||
from xml.etree.ElementTree import ElementTree, ParseError
|
||||
@@ -103,13 +104,21 @@ def get_size(data, default_width=0, default_height=0):
|
||||
root = tree.getroot()
|
||||
|
||||
try:
|
||||
width_attr = root.attrib.get("width", "0")
|
||||
height_attr = root.attrib.get("height", "0")
|
||||
width_attr = root.attrib.get("width", "100%")
|
||||
height_attr = root.attrib.get("height", "100%")
|
||||
if width_attr.endswith("%") or height_attr.endswith("%"):
|
||||
# check to viewBox attribute if width or height value is a percentage
|
||||
_, _, width_attr, height_attr = root.attrib.get("viewBox").split()
|
||||
viewbox = root.attrib.get("viewBox")
|
||||
if not viewbox:
|
||||
raise ValueError("Invalid SVG file: missing viewBox attribute")
|
||||
_, _, viewbox_width, viewbox_height = re.split(r'[\s,]+', viewbox)
|
||||
if width_attr.endswith("%"):
|
||||
width = _svg_convert_size(viewbox_width, width_attr)
|
||||
else:
|
||||
width = _svg_convert_size(width_attr)
|
||||
if height_attr.endswith("%"):
|
||||
height = _svg_convert_size(viewbox_height, height_attr)
|
||||
else:
|
||||
height = _svg_convert_size(height_attr)
|
||||
except (AttributeError, IndexError) as e:
|
||||
raise ValueError("Invalid SVG file: {}".format(e))
|
||||
@@ -117,11 +126,12 @@ def get_size(data, default_width=0, default_height=0):
|
||||
return width, height, filetype
|
||||
|
||||
|
||||
def _svg_convert_size(size):
|
||||
def _svg_convert_size(size, percent=None):
|
||||
"""
|
||||
Convert svg size to the px version
|
||||
|
||||
:param size: String with the size
|
||||
:param percent: String with the percentage, None = 100%
|
||||
"""
|
||||
|
||||
# https://www.w3.org/TR/SVG/coords.html#Units
|
||||
@@ -133,8 +143,11 @@ def _svg_convert_size(size):
|
||||
"in": 90,
|
||||
"px": 1
|
||||
}
|
||||
if len(size) > 3:
|
||||
factor = 1.0
|
||||
if len(size) >= 3:
|
||||
if size[-2:] in conversion_table:
|
||||
return round(float(size[:-2]) * conversion_table[size[-2:]])
|
||||
|
||||
return round(float(size))
|
||||
factor = conversion_table[size[-2:]]
|
||||
size = size[:-2]
|
||||
if percent:
|
||||
factor *= float(percent.rstrip("%")) / 100.0
|
||||
return round(float(size) * factor)
|
||||
@@ -23,8 +23,8 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "2.1.10"
|
||||
__version_info__ = (2, 1, 10, 0)
|
||||
__version__ = "2.1.12"
|
||||
__version_info__ = (2, 1, 12, 0)
|
||||
|
||||
# If it's a git checkout try to add the commit
|
||||
if "dev" in __version__:
|
||||
|
||||
@@ -971,13 +971,12 @@ def test_mount_binds(vm, tmpdir):
|
||||
|
||||
def test_start_vnc(vm, loop):
|
||||
vm.console_resolution = "1280x1024"
|
||||
with patch("shutil.which", return_value="/bin/x"):
|
||||
with patch("shutil.which", return_value="/bin/Xtigervnc"):
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.wait_for_file_creation") as mock_wait:
|
||||
with asyncio_patch("asyncio.create_subprocess_exec") as mock_exec:
|
||||
loop.run_until_complete(asyncio.ensure_future(vm._start_vnc()))
|
||||
assert vm._display is not None
|
||||
mock_exec.assert_any_call("Xvfb", "-nolisten", "tcp", ":{}".format(vm._display), "-screen", "0", "1280x1024x16")
|
||||
mock_exec.assert_any_call("x11vnc", "-forever", "-nopw", "-shared", "-geometry", "1280x1024", "-display", "WAIT:{}".format(vm._display), "-rfbport", str(vm.console), "-rfbportv6", str(vm.console), "-noncache", "-listen", "127.0.0.1")
|
||||
assert mock_exec.call_args[0] == ("Xtigervnc", "-geometry", vm.console_resolution, "-depth", "16", "-interface", "127.0.0.1", "-rfbport", str(vm.console), "-AlwaysShared", "-SecurityTypes", "None", ":{}".format(vm._display))
|
||||
mock_wait.assert_called_with("/tmp/.X11-unix/X{}".format(vm._display))
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user