Compare commits

..

33 Commits

Author SHA1 Message Date
19c4ec1867 1.5.3 2017-01-12 08:18:46 +01:00
8019374ed0 Fix sporadically systemd is unable to start gns3-server
Signed-off-by: Julien Duponchelle <julien@gns3.net>
2017-01-09 10:28:21 +01:00
af530be346 1.5.3dev2 2016-12-20 11:48:55 +01:00
9c3cfc4f4e 1.5.3 rc1 2016-12-20 09:30:52 +01:00
c7d878ed9e Fix TypeError: __init__() got multiple values for argument 'handler' with recent aiohttp
Fix #841
2016-12-20 09:21:02 +01:00
49f1ee2e32 Support aiohttp 1.2 (but not compatible with previous versions)
Fix #840
2016-12-20 09:17:45 +01:00
bd4de862c8 Explain that segfault on IOU is a issue with the image
Fix #739
2016-10-28 11:25:14 +02:00
f038735595 Fix an issue with finding vmrun and vboxmanage
Ref #1575
2016-10-19 17:44:55 +02:00
a4f8675c93 Support named remote servers for VPCS
Fix #722
2016-10-19 16:55:33 +02:00
da71f29208 Merge pull request #726 from GNS3/port_check
When checking for a free port check if the host and 0.0.0.0 are available
2016-10-18 21:02:28 -06:00
b53b34d485 When checking for a free port check if the host and 0.0.0.0 are available
Because some emulators will listen on 0.0.0.0 and not on the host.

Fix #721
2016-10-18 09:59:31 +02:00
e63da227d0 Try pyup.io 2016-10-17 10:27:35 +02:00
c7d9af121f smm=off is only for 64bits
Fix #714
2016-10-04 14:53:09 +02:00
15babb137d Fix set hostname on remote server
Fix #691
2016-09-27 10:40:58 +02:00
eccee6b629 Support unstable ppa for remote install 2016-09-27 10:35:24 +02:00
ef95ba1ed8 Fix sending smm option to qemu
Fix #689
2016-09-26 15:15:38 +02:00
2bbdbeaa82 Workaround a bug with KVM, Qemu >= 2.4 and Intel CPU
Fix #685
2016-09-21 19:25:15 +02:00
de2dad20d5 Renable sleep at Vbox exit bug seem to be back
Fix https://github.com/GNS3/gns3-gui/issues/1444
2016-09-08 18:37:34 +02:00
84c0a17572 Support large project (> 2GB) during export
Fix #670
2016-09-08 11:41:12 +02:00
f0edf799b7 Fix Deleting running telnet docker VM shows error in log
Fix #662
2016-09-07 14:24:56 +02:00
a7be4681d5 Create gns3server/symbols directory
This directory is use only in 2.0 but to simplify packaging
we create it in 1.5.
2016-09-06 09:45:29 +02:00
07b982d4db Fix when closing a container using VNC, root permission are not reset
Fix #659

Signed-off-by: Julien Duponchelle <julien@gns3.net>
2016-09-01 09:28:22 +02:00
da1cd9a3e7 Use $PATH also for dynamips and cleanup some $PATH usages
Fix #655
2016-08-29 11:27:35 +02:00
0eafb6f06c Fix a lock issue with some virtualbox vm
Fix  https://github.com/GNS3/gns3-gui/issues/1444
2016-08-29 10:51:50 +02:00
042a69eecf Raise proper error when you try to load an empty qcow2 file
Fix #637
2016-08-29 10:18:18 +02:00
1885fe62a6 Fix upload form crash
Fix #647
2016-08-29 09:25:02 +02:00
e481ffa94c Search bin from the $PATH for sample configuration file 2016-08-27 18:10:41 +02:00
937bbf0131 Merge pull request #653 from ehlers/master
Update 'Updated systemd unit file and added sample configuration file'
2016-08-27 18:09:35 +02:00
d58a6ccda9 Update 'Updated systemd unit file and added sample configuration file' 2016-08-27 02:00:26 +02:00
84fb108abb Change CR/LF line ending to unix style 2016-08-26 18:45:22 +02:00
4455499e00 Merge pull request #648 from ianc1215/master
Updated systemd unit file and added sample configuration file
2016-08-26 09:28:40 +02:00
763f258465 Updated systemd unit file and added sample configuration file
Rewrote the systemd unit file to fix an issue where the system was not able to create a PID file inside /var/run.

I fixed this by having systemd create a new directory called /var/run/gns3. Then I had systemd change ownership of the directory to gns3:gns3 so the gns3server executable could read and write the PID file. I have tested these changes against Ubuntu 16.04.1 LTS.
2016-08-25 19:24:09 -04:00
d447a04c6a 1.5.3dev1 2016-08-18 22:16:01 +02:00
28 changed files with 306 additions and 74 deletions

2
.pyup.yml Normal file
View File

@ -0,0 +1,2 @@
branch:
2.0

View File

@ -1,5 +1,31 @@
# Change Log
## 1.5.3 12/01/2016
* Fix sporadically systemd is unable to start gns3-server
## 1.5.3 rc1 20/12/2016
* Support aiohttp 1.2 (but not compatible with previous versions)
* Explain that segfault on IOU is a issue with the image
* Fix an issue with finding vmrun and vboxmanage
* Support named remote servers for VPCS
* When checking for a free port check if the host and 0.0.0.0 are available
* smm=off is only for 64bits
* Fix set hostname on remote server
* Fix sending smm option to qemu
* Workaround a bug with KVM, Qemu >= 2.4 and Intel CPU
* Renable sleep at Vbox exit bug seem to be back
* Support large project (> 2GB) during export
* Fix Deleting running telnet docker VM shows error in log
* Fix when closing a container using VNC, root permission are not reset
* Use $PATH also for dynamips and cleanup some $PATH usages
* Fix a lock issue with some virtualbox vm
* Raise proper error when you try to load an empty qcow2 file
* Fix upload form crash
* Search bin from the $PATH for sample configuration file
* Updated systemd unit file and added sample configuration file
## 1.5.2 18/08/2016
* Move utils.vmnet to gns3 namespace

61
conf/gns3_server.conf Normal file
View File

@ -0,0 +1,61 @@
[Server]
; IP where the server listen for connections
host = 0.0.0.0
; HTTP port for controlling the servers
port = 3080
; Option to enable SSL encryption
ssl = False
certfile=/home/gns3/.config/GNS3/ssl/server.cert
certkey=/home/gns3/.config/GNS3/ssl/server.key
; Path where devices images are stored
images_path = /home/gns3/GNS3/images
; Path where user projects are stored
projects_path = /home/gns3/GNS3/projects
; Option to automatically send crash reports to the GNS3 team
report_errors = True
; First console port of the range allocated to devices
console_start_port_range = 5000
; Last console port of the range allocated to devices
console_end_port_range = 10000
; First port of the range allocated for inter-device communication. Two ports are allocated per link.
udp_start_port_range = 10000
; Last port of the range allocated for inter-device communication. Two ports are allocated per link
udp_start_end_range = 20000
; uBridge executable location, default: search in PATH
;ubridge_path = ubridge
; Option to enable HTTP authentication.
auth = False
; Username for HTTP authentication.
user = gns3
; Password for HTTP authentication.
password = gns3
[VPCS]
; VPCS executable location, default: search in PATH
;vpcs_path = vpcs
[Dynamips]
; Enable auxiliary console ports on IOS routers
allocate_aux_console_ports = False
mmap_support = True
; Dynamips executable path, default: search in PATH
;dynamips_path = dynamips
sparse_memory_support = True
ghost_ios_support = True
[IOU]
; iouyap executable path, default: search in PATH
;iouyap_path = iouyap
; Path of your .iourc file. If not provided, the file is searched in $HOME/.iourc
iourc_path = /home/gns3/.iourc
; Validate if the iourc license file is correct. If you turn this off and your licence is invalid IOU will not start and no errors will be shown.
license_check = True
[Qemu]
; !! Remember to add the gns3 user to the KVM group, otherwise you will not have read / write permssions to /dev/kvm !!
enable_kvm = True

View File

@ -52,7 +52,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://76659dafd45f4e3f8ca0ae7597eac70b:dc0094ac9ae44fe1beacc1e37b2f6041@app.getsentry.com/38482"
DSN = "sync+https://700b0c46edb0473baacd2dc318d8de1f:824bd6d75471494ebcb87ce27cfdeade@sentry.io/38482"
if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert):

View File

@ -54,7 +54,8 @@ class UploadHandler:
@Route.post(
r"/upload",
description="Manage upload of GNS3 images",
api_version=None
api_version=None,
raw=True
)
def upload(request, response):
data = yield from request.post()

View File

@ -403,11 +403,10 @@ class BaseVM:
"""
path = self._manager.config.get_section_config("Server").get("ubridge_path", "ubridge")
if path == "ubridge":
path = shutil.which("ubridge")
path = shutil.which(path)
if path is None or len(path) == 0:
raise VMError("uBridge is not installed")
raise VMError("uBridge is not installed or uBridge path is invalid")
return path
@asyncio.coroutine

View File

@ -530,7 +530,11 @@ class DockerVM(BaseVM):
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
yield from self._ubridge_hypervisor.stop()
state = yield from self._get_container_state()
try:
state = yield from self._get_container_state()
except DockerHttp404Error:
state = "stopped"
if state == "paused":
yield from self.unpause()
@ -577,6 +581,9 @@ class DockerVM(BaseVM):
@asyncio.coroutine
def reset(self):
try:
state = yield from self._get_container_state()
if state == "paused" or state == "running":
yield from self.stop()
if self.console_type == "vnc":
if self._x11vnc_process:
try:
@ -589,9 +596,6 @@ class DockerVM(BaseVM):
yield from self._xvfb_process.wait()
except ProcessLookupError:
pass
state = yield from self._get_container_state()
if state == "paused" or state == "running":
yield from self.stop()
# v 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false.
# force - 1/True/true or 0/False/false, Kill then remove the container. Default false.
yield from self.manager.query("DELETE", "containers/{}".format(self._cid), params={"force": 1, "v": 1})
@ -658,7 +662,7 @@ class DockerVM(BaseVM):
if nio.capturing:
yield from self._ubridge_hypervisor.send('bridge start_capture bridge{adapter} "{pcap_file}"'.format(adapter=adapter_number,
pcap_file=nio.pcap_output_file))
pcap_file=nio.pcap_output_file))
yield from self._ubridge_hypervisor.send('bridge start bridge{adapter}'.format(adapter=adapter_number))

View File

@ -336,16 +336,16 @@ class Dynamips(BaseManager):
def find_dynamips(self):
# look for Dynamips
dynamips_path = self.config.get_section_config("Dynamips").get("dynamips_path")
if not dynamips_path:
dynamips_path = shutil.which("dynamips")
dynamips_path = self.config.get_section_config("Dynamips").get("dynamips_path", "dynamips")
if not os.path.isabs(dynamips_path):
dynamips_path = shutil.which(dynamips_path)
if not dynamips_path:
raise DynamipsError("Could not find Dynamips")
if not os.path.isfile(dynamips_path):
raise DynamipsError("Dynamips {} is not accessible".format(dynamips_path))
if not os.access(dynamips_path, os.X_OK):
raise DynamipsError("Dynamips is not executable")
raise DynamipsError("Dynamips {} is not executable".format(dynamips_path))
self._dynamips_path = dynamips_path
return dynamips_path

View File

@ -230,9 +230,11 @@ class IOUVM(BaseVM):
:returns: path to IOUYAP
"""
path = self._manager.config.get_section_config("IOU").get("iouyap_path", "iouyap")
if path == "iouyap":
path = shutil.which("iouyap")
search_path = self._manager.config.get_section_config("IOU").get("iouyap_path", "iouyap")
path = shutil.which(search_path)
# shutil.which return None if the path doesn't exists
if not path:
return search_path
return path
@property
@ -534,14 +536,19 @@ class IOUVM(BaseVM):
:param returncode: Process returncode
"""
log.info("{} process has stopped, return code: {}".format(process_name, returncode))
self._terminate_process_iou()
self._terminate_process_iouyap()
self._ioucon_thread_stop_event.set()
if returncode != 0:
self.project.emit("log.error", {"message": "{} process has stopped, return code: {}\n{}".format(process_name,
returncode,
self.read_iou_stdout())})
log.info("{} process has stopped, return code: {}".format(process_name, returncode))
else:
if returncode == 11:
message = "{} process has stopped, return code: {}. This could be an issue with the image using a different image can fix the issue.\n{}".format(process_name, returncode, self.read_iou_stdout())
else:
message = "{} process has stopped, return code: {}\n{}".format(process_name, returncode, self.read_iou_stdout())
log.warn(message)
self.project.emit("log.error", {"message": message})
def _rename_nvram_file(self):
"""

View File

@ -153,6 +153,8 @@ class PortManager:
try:
PortManager._check_port(host, port, socket_type)
if host != "0.0.0.0":
PortManager._check_port("0.0.0.0", port, socket_type)
return port
except OSError as e:
last_exception = e

View File

@ -528,7 +528,7 @@ class Project:
:returns: ZipStream object
"""
z = zipstream.ZipFile()
z = zipstream.ZipFile(allowZip64=True)
# topdown allo to modify the list of directory in order to ignore
# directory
for root, dirs, files in os.walk(self._path, topdown=True):

View File

@ -62,7 +62,10 @@ class Qcow2:
with open(self.path, 'rb') as f:
content = f.read(struct.calcsize(struct_format))
self.magic, self.version, self.backing_file_offset, self.backing_file_size = struct.unpack_from(struct_format, content)
try:
self.magic, self.version, self.backing_file_offset, self.backing_file_size = struct.unpack_from(struct_format, content)
except struct.error:
raise Qcow2Error("Invalid file header for {}".format(self.path))
if self.magic != 1363560955: # The first 4 bytes contain the characters 'Q', 'F', 'I' followed by 0xfb.
raise Qcow2Error("Invalid magic for {}".format(self.path))

View File

@ -1424,6 +1424,11 @@ class QemuVM(BaseVM):
command.extend(["-smp", "cpus={}".format(self._cpus)])
if self._run_with_kvm(self.qemu_path, self._options):
command.extend(["-enable-kvm"])
version = yield from self.manager.get_qemu_version(self.qemu_path)
# Issue on some combo Intel CPU + KVM + Qemu 2.4.0
# https://github.com/GNS3/gns3-server/issues/685
if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64":
command.extend(["-machine", "smm=off"])
command.extend(["-boot", "order={}".format(self._boot_priority)])
cdrom_option = self._cdrom_option()
command.extend(cdrom_option)

View File

@ -66,7 +66,10 @@ class VirtualBox(BaseManager):
elif sys.platform.startswith("darwin"):
vboxmanage_path = "/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
else:
vboxmanage_path = shutil.which("vboxmanage")
vboxmanage_path = "vboxmanage"
if not os.path.isabs(vboxmanage_path):
vboxmanage_path = shutil.which(vboxmanage_path)
if not vboxmanage_path:
raise VirtualBoxError("Could not find VBoxManage")

View File

@ -30,7 +30,7 @@ import asyncio
from gns3server.utils import parse_version
from gns3server.utils.telnet_server import TelnetServer
from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation
from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation, locked_coroutine
from .virtualbox_error import VirtualBoxError
from ..nios.nio_udp import NIOUDP
from ..nios.nio_nat import NIONAT
@ -230,7 +230,7 @@ class VirtualBoxVM(BaseVM):
if (yield from self.check_hw_virtualization()):
self._hw_virtualization = True
@asyncio.coroutine
@locked_coroutine
def stop(self):
"""
Stops this VirtualBox VM.
@ -250,7 +250,7 @@ class VirtualBoxVM(BaseVM):
log.debug("Stop result: {}".format(result))
log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
# yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
try:
# deactivate the first serial port
yield from self._modify_vm("--uart1 off")

View File

@ -105,7 +105,10 @@ class VMware(BaseManager):
elif sys.platform.startswith("darwin"):
vmrun_path = "/Applications/VMware Fusion.app/Contents/Library/vmrun"
else:
vmrun_path = shutil.which("vmrun")
vmrun_path = "vmrun"
if not os.path.isabs(vmrun_path):
vmrun_path = shutil.which(vmrun_path)
if not vmrun_path:
raise VMwareError("Could not find VMware vmrun, please make sure it is installed")

View File

@ -22,6 +22,7 @@ order to run a VPCS VM.
import os
import sys
import socket
import subprocess
import signal
import re
@ -139,9 +140,11 @@ class VPCSVM(BaseVM):
:returns: path to VPCS
"""
path = self._manager.config.get_section_config("VPCS").get("vpcs_path", "vpcs")
if path == "vpcs":
path = shutil.which("vpcs")
search_path = self._manager.config.get_section_config("VPCS").get("vpcs_path", "vpcs")
path = shutil.which(search_path)
# shutil.which return None if the path doesn't exists
if not path:
return search_path
return path
@BaseVM.name.setter
@ -430,7 +433,10 @@ class VPCSVM(BaseVM):
# UDP tunnel
command.extend(["-s", str(nio.lport)]) # source UDP port
command.extend(["-c", str(nio.rport)]) # destination UDP port
command.extend(["-t", nio.rhost]) # destination host
try:
command.extend(["-t", socket.gethostbyname(nio.rhost)]) # destination host, we need to resolve the hostname because VPCS doesn't support it
except socket.gaierror as e:
raise VPCSError("Can't resolve hostname {}".format(nio.rhost))
elif isinstance(nio, NIOTAP):
# TAP interface

View File

@ -30,7 +30,6 @@ import time
import atexit
from .web.route import Route
from .web.request_handler import RequestHandler
from .config import Config
from .modules import MODULES
from .modules.port_manager import PortManager
@ -199,6 +198,11 @@ class Server:
Starts the server.
"""
server_logger = logging.getLogger('aiohttp.server')
# In debug mode we don't use the standard request log but a more complete in response.py
if log.getEffectiveLevel() == logging.DEBUG:
server_logger.setLevel(logging.CRITICAL)
logger = logging.getLogger("asyncio")
logger.setLevel(logging.ERROR)
@ -239,7 +243,7 @@ class Server:
m.port_manager = self._port_manager
log.info("Starting server on {}:{}".format(self._host, self._port))
self._handler = app.make_handler(handler=RequestHandler)
self._handler = app.make_handler()
server = self._run_application(self._handler, ssl_context)
self._loop.run_until_complete(server)
self._signal_handling()

View File

View File

@ -127,3 +127,24 @@ def wait_for_named_pipe_creation(pipe_path, timeout=60):
else:
return
raise asyncio.TimeoutError()
def locked_coroutine(f):
"""
Method decorator that replace asyncio.coroutine that warranty
that this specific method of this class instance will not we
executed twice at the same time
"""
@asyncio.coroutine
def new_function(*args, **kwargs):
# In the instance of the class we will store
# a lock has an attribute.
lock_var_name = "__" + f.__name__ + "_lock"
if not hasattr(args[0], lock_var_name):
setattr(args[0], lock_var_name, asyncio.Lock())
with (yield from getattr(args[0], lock_var_name)):
return (yield from f(*args, **kwargs))
return new_function

View File

@ -23,5 +23,5 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "1.5.2"
__version_info__ = (1, 5, 2, 0)
__version__ = "1.5.3"
__version_info__ = (1, 5, 3, 0)

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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.web
import logging
class RequestHandler(aiohttp.web.RequestHandler):
def log_access(self, message, environ, response, time):
# In debug mode we don't use the standard request log but a more complete in response.py
if self.logger.getEffectiveLevel() != logging.DEBUG:
super().log_access(message, environ, response, time)

View File

@ -1,14 +1,19 @@
[Unit]
Description=GNS3 server
Wants=network-online.target
After=network.target network-online.target
[Service]
Type=forking
Environment=statedir=/var/cache/gns3
PIDFile=/var/run/gns3.pid
ExecStart=/usr/local/bin/gns3server --log /var/log/gns3.log \
--pid /var/run/gns3.pid --daemon
Restart=on-abort
User=gns3
Group=gns3
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3
ExecStart=/usr/local/bin/gns3server --log /var/log/gns3/gns3.log \
--pid /var/run/gns3/gns3.pid --daemon
Restart=on-abort
PIDFile=/var/run/gns3/gns3.pid
[Install]
WantedBy=multi-user.target

View File

@ -1,5 +1,7 @@
jsonschema>=2.4.0
aiohttp>=0.21.5
aiohttp>=1.2.0
aiohttp_cors>=0.4.0
yarl>=0.7.0
Jinja2>=2.7.3
raven>=5.2.0
psutil>=3.0.0

View File

@ -26,6 +26,7 @@ function help {
echo "--with-openvpn: Install Open VPN" >&2
echo "--with-iou: Install IOU" >&2
echo "--with-i386-repository: Add i386 repositories require by IOU if they are not available on the system. Warning this will replace your source.list in order to use official ubuntu mirror" >&2
echo "--unstable: Use the GNS3 unstable repository"
echo "--help: This help" >&2
}
@ -46,8 +47,9 @@ fi
USE_VPN=0
USE_IOU=0
I386_REPO=0
UNSTABLE=0
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,help -n 'gns3-remote-install.sh' -- "$@"`
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,unstable,help -n 'gns3-remote-install.sh' -- "$@"`
if [ $? != 0 ]
then
help
@ -70,6 +72,10 @@ while true ; do
I386_REPO=1
shift
;;
--unstable)
UNSTABLE=1
shift
;;
-h|--help)
help
exit 1
@ -85,12 +91,23 @@ set -e
export DEBIAN_FRONTEND="noninteractive"
log "Add GNS3 repository"
if [ $UNSTABLE == 1 ]
then
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
deb http://ppa.launchpad.net/gns3/unstable/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/unstable/ubuntu trusty main
deb http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
EOFLIST
else
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
deb http://ppa.launchpad.net/gns3/ppa/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/ppa/ubuntu trusty main
deb http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
EOFLIST
fi
if [ $I386_REPO == 1 ]
then
@ -142,7 +159,7 @@ then
apt-get install -y gns3-iou
# Force the host name to gns3vm
hostnamectl set-hostname gns3vm
echo gns3vm > /etc/hostname
# Force hostid for IOU
dd if=/dev/zero bs=4 count=1 of=/etc/hostid

View File

@ -56,6 +56,12 @@ def test_invalid_file():
Qcow2("tests/resources/nvram_iou")
def test_invalid_empty_file(tmpdir):
open(str(tmpdir / 'a'), 'w+').close()
with pytest.raises(Qcow2Error):
Qcow2(str(tmpdir / 'a'))
@pytest.mark.skipif(qemu_img() is None, reason="qemu-img is not available")
def test_rebase(tmpdir, loop):
shutil.copy("tests/resources/empty8G.qcow2", str(tmpdir / "empty16G.qcow2"))

View File

@ -22,7 +22,7 @@ import os
import sys
import stat
import re
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest import mock
@ -434,6 +434,66 @@ def test_build_command(vm, loop, fake_qemu_binary, port_manager):
]
def test_build_command_kvm(linux_platform, vm, loop, fake_qemu_binary, port_manager):
"""
Qemu 2.4 introduce an issue with KVM
"""
vm._run_with_kvm = MagicMock(return_value=True)
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.3.2")
os.environ["DISPLAY"] = "0:0"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
assert cmd == [
fake_qemu_binary,
"-name",
"test",
"-m",
"256M",
"-smp",
"cpus=1",
"-enable-kvm",
"-boot",
"order=c",
"-serial",
"telnet:127.0.0.1:{},server,nowait".format(vm.console),
"-net",
"none",
"-device",
"e1000,mac={}".format(vm._mac_address)
]
def test_build_command_kvm_2_4(linux_platform, vm, loop, fake_qemu_binary, port_manager):
"""
Qemu 2.4 introduce an issue with KVM
"""
vm._run_with_kvm = MagicMock(return_value=True)
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.4.2")
os.environ["DISPLAY"] = "0:0"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
assert cmd == [
fake_qemu_binary,
"-name",
"test",
"-m",
"256M",
"-smp",
"cpus=1",
"-enable-kvm",
"-machine",
"smm=off",
"-boot",
"order=c",
"-serial",
"telnet:127.0.0.1:{},server,nowait".format(vm.console),
"-net",
"none",
"-device",
"e1000,mac={}".format(vm._mac_address)
]
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_build_command_without_display(vm, loop, fake_qemu_binary):

View File

@ -21,7 +21,7 @@ import pytest
import sys
from unittest.mock import MagicMock
from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_output, wait_for_process_termination
from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_output, wait_for_process_termination, locked_coroutine
def test_wait_run_in_executor(loop):
@ -67,3 +67,26 @@ def test_wait_for_process_termination(loop):
exec = wait_for_process_termination(process, timeout=0.5)
with pytest.raises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.async(exec))
def test_lock_decorator(loop):
"""
The test check if the the second call to method_to_lock wait for the
first call to finish
"""
class TestLock:
def __init__(self):
self._test_val = 0
@locked_coroutine
def method_to_lock(self):
res = self._test_val
yield from asyncio.sleep(0.1)
self._test_val += 1
return res
i = TestLock()
res = set(loop.run_until_complete(asyncio.gather(i.method_to_lock(), i.method_to_lock())))
assert res == set((0, 1,)) # We use a set to test this to avoid order issue