Compare commits

..
42 Commits
Author SHA1 Message Date
Julien Duponchelle f2d5eb0b27 2.0.1 2017-05-16 08:47:35 +02:00
Julien Duponchelle 39419462db Handle HTTP 504 errors from compute node
Fix #1032
2017-05-16 08:37:54 +02:00
Jeremy Grossmann ddc5e8169e Merge pull request #1031 from GNS3/async_images
When connecting to a compute node ask for qemu images list
2017-05-15 21:20:28 +02:00
Julien Duponchelle 3dc478385b When connecting to a compute node ask for qemu images list
The server start to compute qemu image list in background
because first time discovery could be slow due to the compute
time of md5sum.

Fix #996
2017-05-15 15:35:48 +02:00
Julien Duponchelle 851d6e1db8 When importing portable project NAT node is loaded on GNS3 VM
Fix #1030
2017-05-15 10:21:37 +02:00
pyup.io bot facdf9c617 Update sphinx from 1.5.5 to 1.5.6 (#1029) 2017-05-15 09:41:18 +02:00
grossmj 70d34befb9 Merge remote-tracking branch 'origin/2.0' into 2.0 2017-05-12 21:04:39 +08:00
Jeremy Grossmann 8696a464ab Merge pull request #1027 from GNS3/fix_wic_numbering
Fix wic numbering
2017-05-12 21:03:05 +08:00
Julien Duponchelle 5721dd3f30 Fix port numbering for Wic slot 1 & 2 2017-05-12 14:12:24 +02:00
grossmj bf38a8515e Fix typo. 2017-05-12 18:54:54 +08:00
grossmj 942cc09eda Fixes issue with connections when loading an ATM switch. 2017-05-12 18:00:58 +08:00
Jeremy Grossmann 9baaf5685d Merge pull request #1028 from GNS3/filter_logs
Filter /settings from log
2017-05-12 17:31:42 +08:00
Julien Duponchelle 961dbb5162 Filter /settings from log
Fix #1026
2017-05-12 10:48:43 +02:00
Julien Duponchelle 19a6157f64 Fix wic numbering
Fix #1011
2017-05-12 09:48:05 +02:00
grossmj 18cfcaa081 Fixes ATM mapping. 2017-05-12 13:09:26 +08:00
grossmj 87d42f4eb9 Fixes Frame-relay VC mapping. 2017-05-12 12:39:01 +08:00
Julien Duponchelle 766456014b Fix export project is looking into the wrong directory
Fix #1010
2017-05-11 17:59:57 +02:00
Julien Duponchelle f31e46e8ed Fix a race condition in logging when ubridge exit fast
Fix #1025
2017-05-11 17:26:18 +02:00
Julien Duponchelle fb6a95f7bb Fix conversion issue with old topology with C3640
Fix #1019
2017-05-10 13:50:19 +02:00
Julien Duponchelle 93fe66a253 Fix error when you have a directory in your symbols directory
Fix #1006
2017-05-10 13:40:49 +02:00
Julien Duponchelle e870b0cfa0 Catch VMWare errors when getting status after GNS3 VM start
Fix #1008
2017-05-09 19:16:47 +02:00
Julien Duponchelle b4467d83bf Make sure upstart LANG is utf8
Fix #1017
2017-05-09 18:54:01 +02:00
Julien Duponchelle ac5509f383 Always install typing module (compat with python 3.4 on ubuntu)
Fix #1021
2017-05-09 15:09:55 +02:00
Julien Duponchelle 050f6876ce Fix KeyError: 'filename'
Fix #1018
2017-05-09 12:25:15 +02:00
Julien Duponchelle 2172c25df6 Run tests into a tmp directory prefixed by unicode character 2017-05-09 09:43:47 +02:00
grossmj 48dae085aa Catch missing function listxattr on some Linux host. 2017-05-08 21:02:23 +07:00
Julien Duponchelle bfee08459d Test image sorting
Ref https://github.com/GNS3/gns3-gui/issues/2030
2017-05-05 20:09:51 +02:00
Julien Duponchelle 8a6584d90f Sort image list
Fix https://github.com/GNS3/gns3-gui/issues/2030
2017-05-05 19:52:45 +02:00
Julien Duponchelle 6870749e50 Handle a race condition at project closing
Fix #1015
2017-05-05 19:39:06 +02:00
Julien Duponchelle 3070a68273 Fix unicode error when reading ios configuration
Fix #1013
2017-05-05 19:27:04 +02:00
Julien Duponchelle bda36081a9 Fix AttributeError: 'NoneType' object has no attribute 'send'
Fix #1014
2017-05-05 17:16:07 +02:00
Julien Duponchelle 930e7089db Fix a conversion issue with some 1.3 topologies 2017-05-04 12:09:56 +02:00
Julien Duponchelle 04220846b3 Fix an error with ethernetswitch when ethertype is null
Fix #1007
2017-05-04 10:33:02 +02:00
Julien Duponchelle 694a4e8068 Raise an error if we can't create the dynamips configuration directory
Fix #992
2017-05-03 17:55:13 +02:00
Julien Duponchelle 7ba351c888 Catch timeout error when loading virtualbox VM
Fix #999
2017-05-03 17:44:55 +02:00
Julien Duponchelle ae3e25b03b Handle broken compute at server startup
Fix #1001
2017-05-03 17:40:58 +02:00
Julien Duponchelle fa1450cb24 Catch error when we can't backup the topology
Fix #998
2017-05-03 17:35:10 +02:00
Julien Duponchelle 8e5f7d0838 Catch error when writting the topology file on read only device
Fix #1003
2017-05-03 17:28:47 +02:00
Julien Duponchelle 31d1696215 Catch a race condition in VirtualBox when editing and linking
Fix #993
2017-05-03 16:14:42 +02:00
Julien Duponchelle 078a6d8d34 Fix a race condition when editing a qemu vm and connecting it
Fix #997
2017-05-03 16:01:51 +02:00
Bernhard Ehlers ecba758c58 Docker aux console is a VT100 terminal
Without that program like nano can't work.

Signed-off-by: Julien Duponchelle <julien@gns3.net>
2017-05-03 10:30:43 +02:00
Julien Duponchelle 6190b9c189 2.0.1dev1 2017-05-03 10:18:26 +02:00
36 changed files with 255 additions and 78 deletions

No files matched your search

+2 -1
View File
@@ -9,7 +9,8 @@ install:
- python setup.py install
- pip install -rdev-requirements.txt
script:
- py.test -v -s tests
- mkdir ‡
- py.test -v -s tests --basetemp=‡
deploy:
provider: pypi
user: noplay
+33
View File
@@ -1,5 +1,38 @@
# Change Log
## 2.0.1 16/05/2017
* Handle HTTP 504 errors from compute node
* When connecting to a compute node ask for qemu images list
* When importing portable project NAT node is loaded on GNS3 VM
* Fix port numbering for Wic slot 1 & 2
* Fixes issue with connections when loading an ATM switch.
* Fixes ATM mapping.
* Fixes Frame-relay VC mapping.
* Fix export project is looking into the wrong directory
* Fix a race condition in logging when ubridge exit fast
* Fix conversion issue with old topology with C3640
* Fix error when you have a directory in your symbols directory
* Catch VMWare errors when getting status after GNS3 VM start
* Make sure upstart LANG is utf8
* Always install typing module (compat with python 3.4 on ubuntu)
* Fix KeyError: 'filename'
* Catch missing function listxattr on some Linux host.
* Sort image list
* Handle a race condition at project closing
* Fix unicode error when reading ios configuration
* Fix AttributeError: 'NoneType' object has no attribute 'send'
* Fix a conversion issue with some 1.3 topologies
* Fix an error with ethernetswitch when ethertype is null
* Raise an error if we can't create the dynamips configuration directory
* Catch timeout error when loading virtualbox VM
* Handle broken compute at server startup
* Catch error when we can't backup the topology
* Catch error when writting the topology file on read only device
* Catch a race condition in VirtualBox when editing and linking
* Fix a race condition when editing a qemu vm and connecting it
* Docker aux console is a VT100 terminal
## 2.0.0 02/05/2017
* Fix connection to websocket with last docker release
+1 -1
View File
@@ -1,6 +1,6 @@
-rrequirements.txt
sphinx==1.5.5
sphinx==1.5.6
pytest==3.0.7
pep8==1.7.0
pytest-catchlog==1.2.2
+1 -1
View File
@@ -344,7 +344,7 @@ class BaseManager:
# test the 2nd byte and check if the 13th bit (CAP_NET_RAW) is set
if struct.unpack("<IIIII", caps)[1] & 1 << 13:
return True
except OSError as e:
except (AttributeError, OSError) as e:
log.error("could not determine if CAP_NET_RAW capability is set for {}: {}".format(executable, e))
return False
+3 -2
View File
@@ -541,8 +541,9 @@ class BaseNode:
self._ubridge_hypervisor = Hypervisor(self._project, self.ubridge_path, self.working_dir, server_host)
log.info("Starting new uBridge hypervisor {}:{}".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
yield from self._ubridge_hypervisor.start()
log.info("Hypervisor {}:{} has successfully started".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
yield from self._ubridge_hypervisor.connect()
if self._ubridge_hypervisor:
log.info("Hypervisor {}:{} has successfully started".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
yield from self._ubridge_hypervisor.connect()
@asyncio.coroutine
def _stop_ubridge(self):
+1 -1
View File
@@ -393,7 +393,7 @@ 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
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do /gns3/bin/busybox sh; done", "/dev/null",
"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)
+45 -20
View File
@@ -188,22 +188,33 @@ class ATMSwitch(Device):
raise DynamipsError("Port {} is not allocated".format(port_number))
# remove VCs mapped with the port
pvc_entry = re.compile(r"""^([0-9]*):([0-9]*):([0-9]*)$""")
for source, destination in self._active_mappings.copy().items():
match_source_pvc = pvc_entry.search(source)
match_destination_pvc = pvc_entry.search(destination)
if match_source_pvc and match_destination_pvc:
if len(source) == 3 and len(destination) == 3:
# remove the virtual channels mapped with this port/nio
source_port, source_vpi, source_vci = map(int, match_source_pvc.group(1, 2, 3))
destination_port, destination_vpi, destination_vci = map(int, match_destination_pvc.group(1, 2, 3))
source_port, source_vpi, source_vci = source
destination_port, destination_vpi, destination_vci = destination
if port_number == source_port:
log.info('ATM switch "{name}" [{id}]: unmapping VCC between port {source_port} VPI {source_vpi} VCI {source_vci} and port {destination_port} VPI {destination_vpi} VCI {destination_vci}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_vpi=source_vpi,
source_vci=source_vci,
destination_port=destination_port,
destination_vpi=destination_vpi,
destination_vci=destination_vci))
yield from self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
yield from self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
else:
# remove the virtual paths mapped with this port/nio
source_port, source_vpi = map(int, source.split(':'))
destination_port, destination_vpi = map(int, destination.split(':'))
source_port, source_vpi = source
destination_port, destination_vpi = destination
if port_number == source_port:
log.info('ATM switch "{name}" [{id}]: unmapping VPC between port {source_port} VPI {source_vpi} and port {destination_port} VPI {destination_vpi}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_vpi=source_vpi,
destination_port=destination_port,
destination_vpi=destination_vpi))
yield from self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
yield from self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
@@ -239,6 +250,14 @@ class ATMSwitch(Device):
if self.has_port(destination_port):
if (source_port, source_vpi, source_vci) not in self._active_mappings and \
(destination_port, destination_vpi, destination_vci) not in self._active_mappings:
log.info('ATM switch "{name}" [{id}]: mapping VCC between port {source_port} VPI {source_vpi} VCI {source_vci} and port {destination_port} VPI {destination_vpi} VCI {destination_vci}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_vpi=source_vpi,
source_vci=source_vci,
destination_port=destination_port,
destination_vpi=destination_vpi,
destination_vci=destination_vci))
yield from self.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
yield from self.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
else:
@@ -247,6 +266,12 @@ class ATMSwitch(Device):
destination_port, destination_vpi = map(int, destination.split(':'))
if self.has_port(destination_port):
if (source_port, source_vpi) not in self._active_mappings and (destination_port, destination_vpi) not in self._active_mappings:
log.info('ATM switch "{name}" [{id}]: mapping VPC between port {source_port} VPI {source_vpi} and port {destination_port} VPI {destination_vpi}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_vpi=source_vpi,
destination_port=destination_port,
destination_vpi=destination_vpi))
yield from self.map_vp(source_port, source_vpi, destination_port, destination_vpi)
yield from self.map_vp(destination_port, destination_vpi, source_port, source_vpi)
@@ -262,10 +287,10 @@ class ATMSwitch(Device):
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
return
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
return
nio1 = self._nios[port1]
nio2 = self._nios[port2]
@@ -283,7 +308,7 @@ class ATMSwitch(Device):
port2=port2,
vpi2=vpi2))
self._active_mappings["{}:{}".format(port1, vpi1)] = "{}:{}".format(port2, vpi2)
self._active_mappings[(port1, vpi1)] = (port2, vpi2)
@asyncio.coroutine
def unmap_vp(self, port1, vpi1, port2, vpi2):
@@ -297,10 +322,10 @@ class ATMSwitch(Device):
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
return
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
return
nio1 = self._nios[port1]
nio2 = self._nios[port2]
@@ -318,7 +343,7 @@ class ATMSwitch(Device):
port2=port2,
vpi2=vpi2))
del self._active_mappings["{}:{}".format(port1, vpi1)]
del self._active_mappings[(port1, vpi1)]
@asyncio.coroutine
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@@ -334,10 +359,10 @@ class ATMSwitch(Device):
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
return
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
return
nio1 = self._nios[port1]
nio2 = self._nios[port2]
@@ -359,7 +384,7 @@ class ATMSwitch(Device):
vpi2=vpi2,
vci2=vci2))
self._active_mappings["{}:{}:{}".format(port1, vpi1, vci1)] = "{}:{}:{}".format(port2, vpi2, vci2)
self._active_mappings[(port1, vpi1, vci1)] = (port2, vpi2, vci2)
@asyncio.coroutine
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@@ -375,10 +400,10 @@ class ATMSwitch(Device):
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
return
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
return
nio1 = self._nios[port1]
nio2 = self._nios[port2]
@@ -399,7 +424,7 @@ class ATMSwitch(Device):
port2=port2,
vpi2=vpi2,
vci2=vci2))
del self._active_mappings["{}:{}:{}".format(port1, vpi1, vci1)]
del self._active_mappings[(port1, vpi1, vci1)]
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
+2 -2
View File
@@ -103,8 +103,8 @@ class Bridge(Device):
:param nio: NIO instance to remove
"""
yield from self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
if self._hypervisor:
yield from self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
self._nios.remove(nio)
@property
@@ -246,7 +246,7 @@ class EthernetSwitch(Device):
elif settings["type"] == "dot1q":
yield from self.set_dot1q_port(port_number, settings["vlan"])
elif settings["type"] == "qinq":
yield from self.set_qinq_port(port_number, settings["vlan"], settings["ethertype"])
yield from self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
@asyncio.coroutine
def set_access_port(self, port_number, vlan_id):
@@ -191,9 +191,15 @@ class FrameRelaySwitch(Device):
# remove VCs mapped with the port
for source, destination in self._active_mappings.copy().items():
source_port, source_dlci = map(int, source.split(':'))
destination_port, destination_dlci = map(int, destination.split(':'))
source_port, source_dlci = source
destination_port, destination_dlci = destination
if port_number == source_port:
log.info('Frame Relay switch "{name}" [{id}]: unmapping VC between port {source_port} DLCI {source_dlci} and port {destination_port} DLCI {destination_dlci}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_dlci=source_dlci,
destination_port=destination_port,
destination_dlci=destination_dlci))
yield from self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
yield from self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
@@ -224,6 +230,13 @@ class FrameRelaySwitch(Device):
destination_port, destination_dlci = map(int, destination.split(':'))
if self.has_port(destination_port):
if (source_port, source_dlci) not in self._active_mappings and (destination_port, destination_dlci) not in self._active_mappings:
log.info('Frame Relay switch "{name}" [{id}]: mapping VC between port {source_port} DLCI {source_dlci} and port {destination_port} DLCI {destination_dlci}'.format(name=self._name,
id=self._id,
source_port=source_port,
source_dlci=source_dlci,
destination_port=destination_port,
destination_dlci=destination_dlci))
yield from self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
yield from self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
@@ -260,7 +273,7 @@ class FrameRelaySwitch(Device):
port2=port2,
dlci2=dlci2))
self._active_mappings["{}:{}".format(port1, dlci1)] = "{}:{}".format(port2, dlci2)
self._active_mappings[(port1, dlci1)] = (port2, dlci2)
@asyncio.coroutine
def unmap_vc(self, port1, dlci1, port2, dlci2):
@@ -294,7 +307,7 @@ class FrameRelaySwitch(Device):
dlci1=dlci1,
port2=port2,
dlci2=dlci2))
del self._active_mappings["{}:{}".format(port1, dlci1)]
del self._active_mappings[(port1, dlci1)]
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
+5 -2
View File
@@ -71,7 +71,10 @@ class Router(BaseNode):
super().__init__(name, node_id, project, manager, console=console, aux=aux, allocate_aux=aux)
self._working_directory = os.path.join(self.project.module_working_directory(self.manager.module_name.lower()), self.id)
os.makedirs(os.path.join(self._working_directory, "configs"), exist_ok=True)
try:
os.makedirs(os.path.join(self._working_directory, "configs"), exist_ok=True)
except OSError as e:
raise DynamipsError("Can't create the dynamips config directory: {}".format(str(e)))
if dynamips_id:
self._convert_before_2_0_0_b3(dynamips_id)
@@ -1553,7 +1556,7 @@ class Router(BaseNode):
try:
startup_config_path = os.path.join(self._working_directory, startup_config)
if os.path.exists(startup_config_path):
with open(startup_config_path) as f:
with open(startup_config_path, encoding="utf-8") as f:
self._startup_config_content = f.read()
else:
self._startup_config_content = ''
+7 -3
View File
@@ -1131,9 +1131,13 @@ class QemuVM(BaseNode):
adapter_number=adapter_number))
if self.ubridge:
yield from self._add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
try:
yield from self._add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
except IndexError:
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
elif self.is_running():
raise QemuError("Sorry, adding a link to a started Qemu VM is not supported without using uBridge.")
+1 -1
View File
@@ -94,7 +94,7 @@ class VirtualBox(BaseManager):
if not vboxmanage_path:
vboxmanage_path = self.find_vboxmanage()
if not vboxmanage_path:
raise VirtualBoxError("Could not found VBoxManage")
raise VirtualBoxError("Could not find VBoxManage")
command = [vboxmanage_path, "--nologo", subcommand]
command.extend(args)
@@ -968,9 +968,13 @@ class VirtualBoxVM(BaseNode):
adapter_number=adapter_number))
if self.ubridge:
yield from self._add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
try:
yield from self._add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
except KeyError:
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
else:
vm_state = yield from self._get_vm_state()
+1 -1
View File
@@ -87,7 +87,7 @@ class Controller:
for c in computes:
try:
yield from self.add_compute(**c)
except (aiohttp.web_exceptions.HTTPConflict):
except (aiohttp.web_exceptions.HTTPConflict, KeyError):
pass # Skip not available servers at loading
yield from self.load_projects()
try:
+5 -1
View File
@@ -23,6 +23,7 @@ import json
import uuid
import sys
import io
from operator import itemgetter
from ..utils import parse_version
from ..utils.images import list_images
@@ -528,7 +529,7 @@ class Compute:
raise aiohttp.web.HTTPForbidden(text=msg)
elif response.status == 404:
raise aiohttp.web.HTTPNotFound(text="{} {} not found".format(method, path))
elif response.status == 408:
elif response.status == 408 or response.status == 504:
raise aiohttp.web.HTTPRequestTimeout(text="{} {} request timeout".format(method, path))
elif response.status == 409:
try:
@@ -600,6 +601,9 @@ class Compute:
for local_image in list_images(type):
if local_image['filename'] not in [i['filename'] for i in images]:
images.append(local_image)
images = sorted(images, key=itemgetter('filename'))
else:
images = sorted(images, key=itemgetter('image'))
except OSError as e:
raise ComputeError("Can't list images: {}".format(str(e)))
return images
+2 -3
View File
@@ -190,6 +190,5 @@ def _export_images(project, image, z):
if os.path.exists(path):
arcname = os.path.join("images", directory, os.path.basename(image))
z.write(path, arcname)
break
else:
raise aiohttp.web.HTTPConflict(text="Topology could not be exported because the image {} is not available. If you use multiple server, we need a copy of the image on the main server.".format(path))
return
raise aiohttp.web.HTTPConflict(text="Topology could not be exported because the image {} is not available. If you use multiple server, we need a copy of the image on the main server.".format(image))
@@ -228,7 +228,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
try:
resp = None
resp = yield from session.get('http://127.0.0.1:{}/v2/compute/network/interfaces'.format(api_port))
except (OSError, aiohttp.errors.ClientHttpProcessingError, TimeoutError):
except (OSError, aiohttp.errors.ClientHttpProcessingError, TimeoutError, asyncio.TimeoutError):
pass
if resp:
@@ -118,7 +118,10 @@ class VMwareGNS3VM(BaseGNS3VM):
if vmware_tools_state not in ("installed", "running"):
raise GNS3VMError("VMware tools are not installed in {}".format(self.vmname))
running = yield from self._is_running()
try:
running = yield from self._is_running()
except VMwareError as e:
raise GNS3VMError("Could not list VMware VMs: {}".format(str(e)))
if not running:
log.info("Update GNS3 VM settings")
# set the number of vCPUs and amount of RAM
+1 -1
View File
@@ -114,7 +114,7 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
# unless it's a linux host without GNS3 VM
if not sys.platform.startswith("linux") or controller.has_compute("vm"):
for node in topology["topology"]["nodes"]:
if node["node_type"] in ("docker", "qemu", "iou"):
if node["node_type"] in ("docker", "qemu", "iou", "nat"):
node["compute_id"] = "vm"
else:
for node in topology["topology"]["nodes"]:
+4
View File
@@ -70,6 +70,10 @@ class Port:
return self._name
return self.short_name_type + "{}/{}".format(self._interface_number, self._port_number)
@short_name.setter
def short_name(self, val):
self._short_name = val
def __json__(self):
return {
"name": self._name,
+11 -3
View File
@@ -181,7 +181,9 @@ class DynamipsPortFactory:
ports = []
adapter_number = 0
wic_port_number = 16
wic_slot = 1
wic_port_number = wic_slot * 16
display_wic_port_number = 0
for name in sorted(properties.keys()):
if name.startswith("slot") and properties[name]:
port_class = cls.ADAPTER_MATRIX[properties[name]]["port"]
@@ -194,7 +196,13 @@ class DynamipsPortFactory:
port_class = cls.WIC_MATRIX[properties[name]]["port"]
if port_class:
for port_number in range(0, cls.WIC_MATRIX[properties[name]]["nb_ports"]):
name = "{}{}/{}".format(port_class.long_name_type(), 0, wic_port_number)
ports.append(port_class(name, 0, 0, wic_port_number))
name = "{}{}/{}".format(port_class.long_name_type(), 0, display_wic_port_number)
port = port_class(name, 0, 0, wic_port_number)
port.short_name = "{}{}/{}".format(port.short_name_type, 0, display_wic_port_number)
ports.append(port)
display_wic_port_number += 1
wic_port_number += 1
wic_slot += 1
wic_port_number = wic_slot * 16
return ports
+2
View File
@@ -55,6 +55,8 @@ class Symbols:
for file in os.listdir(directory):
if file.startswith('.'):
continue
if not os.path.isfile(os.path.join(directory, file)):
continue
symbol_id = file
symbols.append({
'symbol_id': symbol_id,
+13 -5
View File
@@ -125,7 +125,10 @@ def load_topology(path):
if "revision" not in topo or topo["revision"] < GNS3_FILE_FORMAT_REVISION:
# If it's an old GNS3 file we need to convert it
# first we backup the file
shutil.copy(path, path + ".backup{}".format(topo.get("revision", 0)))
try:
shutil.copy(path, path + ".backup{}".format(topo.get("revision", 0)))
except (OSError) as e:
raise aiohttp.web.HTTPConflict(text="Can't write backup of the topology {}: {}".format(path, str(e)))
changed = True
if "revision" not in topo or topo["revision"] < 5:
@@ -146,8 +149,11 @@ def load_topology(path):
raise e
if changed:
with open(path, "w+", encoding="utf-8") as f:
json.dump(topo, f, indent=4, sort_keys=True)
try:
with open(path, "w+", encoding="utf-8") as f:
json.dump(topo, f, indent=4, sort_keys=True)
except (OSError) as e:
raise aiohttp.web.HTTPConflict(text="Can't write the topology {}: {}".format(path, str(e)))
return topo
@@ -300,7 +306,7 @@ def _convert_1_3_later(topo, topo_path):
node["console_type"] = None
node["symbol"] = ":/symbols/hub.svg"
node["properties"]["ports_mapping"] = []
for port in old_node["ports"]:
for port in old_node.get("ports", []):
node["properties"]["ports_mapping"].append({
"name": "Ethernet{}".format(port["port_number"] - 1),
"port_number": port["port_number"] - 1
@@ -321,13 +327,15 @@ def _convert_1_3_later(topo, topo_path):
node["node_type"] = "frame_relay_switch"
node["symbol"] = ":/symbols/frame_relay_switch.svg"
node["console_type"] = None
elif old_node["type"] in ["C1700", "C2600", "C2691", "C3600", "C3725", "C3745", "C7200", "EtherSwitchRouter"]:
elif old_node["type"] in ["C1700", "C2600", "C2691", "C3600", "C3620", "C3640", "C3660", "C3725", "C3745", "C7200", "EtherSwitchRouter"]:
if node["symbol"] is None:
node["symbol"] = ":/symbols/router.svg"
node["node_type"] = "dynamips"
node["properties"]["dynamips_id"] = old_node.get("dynamips_id")
if "platform" not in node["properties"] and old_node["type"].startswith("C"):
node["properties"]["platform"] = old_node["type"].lower()
if node["properties"]["platform"].startswith("c36"):
node["properties"]["platform"] = "c3600"
if "ram" not in node["properties"] and old_node["type"].startswith("C"):
node["properties"]["ram"] = PLATFORMS_DEFAULT_RAM[old_node["type"].lower()]
elif old_node["type"] == "VMwareVM":
+1 -1
View File
@@ -54,7 +54,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://fd4397dee2e145da9227af29df24ded1:61a0e2c9b9f64204bb8ef7ac17b98e3e@sentry.io/38482"
DSN = "sync+https://9b1156a90ee943eba20e032cf007297d:024b75c7b11844a58df147a4fb059774@sentry.io/38482"
if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert):
@@ -112,7 +112,10 @@ class ProjectHandler:
if ProjectHandler._notifications_listening.setdefault(project.id, 0) <= 1:
yield from project.close()
pm.remove_project(project.id)
del ProjectHandler._notifications_listening[project.id]
try:
del ProjectHandler._notifications_listening[project.id]
except KeyError:
pass
else:
log.warning("Skip project closing, another client is listening for project notifications")
response.set_status(204)
+2 -2
View File
@@ -23,7 +23,7 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.0.0"
__version__ = "2.0.1"
# If it's a git checkout try to add the commit
if "dev" in __version__:
@@ -36,4 +36,4 @@ if "dev" in __version__:
except Exception as e:
print(e)
__version_info__ = (2, 0, 0, 0)
__version_info__ = (2, 0, 1, 0)
+12
View File
@@ -98,6 +98,16 @@ class WinStreamHandler(logging.StreamHandler):
self.handleError(record)
class LogFilter:
"""
This filter some noise from the logs
"""
def filter(record):
if record.name == "aiohttp.access" and "/settings" in record.msg and "200" in record.msg:
return 0
return 1
def init_logger(level, logfile=None, quiet=False):
if logfile and len(logfile) > 0:
stream_handler = logging.FileHandler(logfile)
@@ -111,5 +121,7 @@ def init_logger(level, logfile=None, quiet=False):
if quiet:
stream_handler.addFilter(logging.Filter(name="user_facing"))
logging.getLogger('user_facing').propagate = False
if level > logging.DEBUG:
stream_handler.addFilter(LogFilter)
logging.basicConfig(level=level, handlers=[stream_handler])
return logging.getLogger('user_facing')
+5
View File
@@ -33,6 +33,7 @@ from .route import Route
from ..config import Config
from ..compute import MODULES
from ..compute.port_manager import PortManager
from ..compute.qemu import Qemu
from ..controller import Controller
@@ -192,6 +193,10 @@ class WebServer:
Called when the HTTP server start
"""
yield from Controller.instance().start()
# Because with a large image collection
# without md5sum already computed we start the
# computing with server start
asyncio.async(Qemu.instance().list_images())
def run(self):
"""
+1
View File
@@ -1,5 +1,6 @@
description "GNS3 server"
author "GNS3 Team"
env LANG=en_US.UTF-8
start on filesystem or runlevel [2345]
stop on shutdown
+1
View File
@@ -6,3 +6,4 @@ Jinja2>=2.7.3
raven>=5.23.0
psutil>=3.0.0
zipstream>=1.1.4
typing>=3.5.3.0 # Otherwise yarl fail with python 3.4
-3
View File
@@ -40,9 +40,6 @@ class PyTest(TestCommand):
dependencies = open("requirements.txt", "r").read().splitlines()
if sys.version_info <= (3, 4):
dependencies.append('typing>=3.5.3.0 # Otherwise yarl fail with python 3.4')
setup(
name="gns3-server",
version=__import__("gns3server").__version__,
+1 -1
View File
@@ -879,7 +879,7 @@ def test_start_aux(vm, loop):
with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=MagicMock()) as mock_exec:
loop.run_until_complete(asyncio.async(vm._start_aux()))
mock_exec.assert_called_with('docker', 'exec', '-i', 'e90e34656842', '/gns3/bin/busybox', 'script', '-qfc', 'while true; do /gns3/bin/busybox sh; done', '/dev/null', stderr=asyncio.subprocess.STDOUT, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE)
mock_exec.assert_called_with('docker', 'exec', '-i', 'e90e34656842', '/gns3/bin/busybox', 'script', '-qfc', 'while true; do TERM=vt100 /gns3/bin/busybox sh; done', '/dev/null', stderr=asyncio.subprocess.STDOUT, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE)
def test_create_network_interfaces(vm):
+4 -2
View File
@@ -360,8 +360,10 @@ def test_images(compute, async_run, images_dir):
images = async_run(compute.images("qemu"))
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=False, timeout=None)
assert images == [{"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0},
{"filename": "asa.qcow2", "path": "asa.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0}]
assert images == [
{"filename": "asa.qcow2", "path": "asa.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0},
{"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0}
]
def test_list_files(project, async_run, compute):
+44
View File
@@ -252,6 +252,50 @@ def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller)
assert topo["topology"]["nodes"][0]["compute_id"] == "vm"
def test_import_nat_non_linux(windows_platform, async_run, tmpdir, controller):
"""
On non linux host NAT should be moved to the GNS3 VM
"""
project_id = str(uuid.uuid4())
controller._computes["vm"] = AsyncioMagicMock()
topology = {
"project_id": str(uuid.uuid4()),
"name": "test",
"type": "topology",
"topology": {
"nodes": [
{
"compute_id": "local",
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
"node_type": "nat",
"name": "test",
"properties": {}
}
],
"links": [],
"computes": [],
"drawings": []
},
"revision": 5,
"version": "2.0.0"
}
with open(str(tmpdir / "project.gns3"), 'w+') as f:
json.dump(topology, f)
zip_path = str(tmpdir / "project.zip")
with zipfile.ZipFile(zip_path, 'w') as myzip:
myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f))
with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f)
assert topo["topology"]["nodes"][0]["compute_id"] == "vm"
def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller):
"""
On non linux host IOU should be moved to the GNS3 VM
+10 -10
View File
@@ -548,8 +548,8 @@ def test_list_ports_dynamips(project, compute):
"link_type": "ethernet"
},
{
"name": "Serial0/16",
"short_name": "s0/16",
"name": "Serial0/0",
"short_name": "s0/0",
"data_link_types": {
"Cisco HDLC": "DLT_C_HDLC",
"Cisco PPP": "DLT_PPP_SERIAL",
@@ -559,8 +559,8 @@ def test_list_ports_dynamips(project, compute):
"link_type": "serial"
},
{
"name": "Serial0/17",
"short_name": "s0/17",
"name": "Serial0/1",
"short_name": "s0/1",
"data_link_types": {
"Cisco HDLC": "DLT_C_HDLC",
"Cisco PPP": "DLT_PPP_SERIAL",
@@ -570,24 +570,24 @@ def test_list_ports_dynamips(project, compute):
"link_type": "serial"
},
{
"name": "Serial0/18",
"short_name": "s0/18",
"name": "Serial0/2",
"short_name": "s0/2",
"data_link_types": {
"Cisco HDLC": "DLT_C_HDLC",
"Cisco PPP": "DLT_PPP_SERIAL",
"Frame Relay": "DLT_FRELAY"},
"port_number": 18,
"port_number": 32,
"adapter_number": 0,
"link_type": "serial"
},
{
"name": "Serial0/19",
"short_name": "s0/19",
"name": "Serial0/3",
"short_name": "s0/3",
"data_link_types": {
"Cisco HDLC": "DLT_C_HDLC",
"Cisco PPP": "DLT_PPP_SERIAL",
"Frame Relay": "DLT_FRELAY"},
"port_number": 19,
"port_number": 33,
"adapter_number": 0,
"link_type": "serial"
}