mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-06-24 17:55:15 +00:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
19c4ec1867 | |||
8019374ed0 | |||
af530be346 | |||
9c3cfc4f4e | |||
c7d878ed9e | |||
49f1ee2e32 | |||
bd4de862c8 | |||
f038735595 | |||
a4f8675c93 | |||
da71f29208 | |||
b53b34d485 | |||
e63da227d0 | |||
c7d9af121f | |||
15babb137d | |||
eccee6b629 | |||
ef95ba1ed8 | |||
2bbdbeaa82 | |||
de2dad20d5 | |||
84c0a17572 | |||
f0edf799b7 | |||
a7be4681d5 | |||
07b982d4db | |||
da1cd9a3e7 | |||
0eafb6f06c | |||
042a69eecf | |||
1885fe62a6 | |||
e481ffa94c | |||
937bbf0131 | |||
d58a6ccda9 | |||
84fb108abb | |||
4455499e00 | |||
763f258465 | |||
d447a04c6a |
26
CHANGELOG
26
CHANGELOG
@ -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
61
conf/gns3_server.conf
Normal 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
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
0
gns3server/symbols/.gitkeep
Normal file
0
gns3server/symbols/.gitkeep
Normal 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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user