Compare commits

...
124 Commits
Author SHA1 Message Date
Julien Duponchelle 78e1321bae 2.0.2 2017-05-30 08:57:07 +02:00
pyup.io bot c46a9da769 Update sphinx from 1.6.1 to 1.6.2 (#1049) 2017-05-29 09:01:12 +02:00
Julien Duponchelle ef5c013561 Merge branch 'master' into 2.0 2017-05-26 10:47:57 +02:00
Julien Duponchelle 8bbe7e8041 Set correct permission on ubridge when doing a remote installation 2017-05-26 10:47:25 +02:00
Julien Duponchelle 777361c23d Merge branch 'master' into 2.0 2017-05-26 10:32:26 +02:00
Julien Duponchelle c3d5abd104 Remote install script should be totally non interactive 2017-05-26 10:29:33 +02:00
Julien Duponchelle 39879ca92b Duplicate project on remote server use UUID
Ref https://github.com/GNS3/gns3-server/issues/1040
2017-05-23 17:20:51 +02:00
Julien Duponchelle 5a86277a9f Test that import erase the auto_open and auto_start setting
Ref #1041
2017-05-23 16:08:30 +02:00
Julien Duponchelle b87692acce Fix import of some old topologies from 1.3 2017-05-23 14:05:47 +02:00
Julien Duponchelle d765d02f06 Fix error in logging of error during starting GNS3 VM 2017-05-23 13:39:41 +02:00
Julien Duponchelle 5bb6c51698 Fix an error when logging Docker container fail to start 2017-05-23 13:31:04 +02:00
Julien Duponchelle 59488e8064 Fix tests on python 3.4 (#1046) 2017-05-23 12:15:04 +02:00
Julien Duponchelle 54e80d68f8 Fix test failed on Python 3.4
Fix #1045
2017-05-23 11:11:13 +02:00
Julien Duponchelle 2a8911d19e Keep DOCKER_MINIMUM_API_VERSION and DOCKER_MINIMUM_VERSION consistent 2017-05-23 11:00:15 +02:00
Bernhard Ehlers 8b7209ea78 Use docker version in error message of outdated docker installation 2017-05-23 10:41:42 +02:00
Bernhard Ehlers 7d48eba01b Use versioned docker API
Since docker v1.13 the unversioned docker API is deprecated.
Backport of gns3-server 2.0, commit effbe59
2017-05-23 10:37:47 +02:00
Julien Duponchelle 1e598d1890 Test for support of docker commit images
Ref #1039
2017-05-23 10:23:12 +02:00
Bernhard Ehlers 2191c487b6 Support images created by "docker commit". Fixes #1039 2017-05-23 10:23:11 +02:00
pyup.io bot 3ed49bbdd5 Update pytest from 3.0.7 to 3.1.0 (#1044) 2017-05-23 08:29:36 +02:00
Julien Duponchelle 7d30da5d4e Do not wait auto start to finish in order to complete project opening
Fix https://github.com/GNS3/gns3-gui/issues/2074
2017-05-22 20:18:07 +02:00
Julien Duponchelle 4dc55a5af4 Improve logging for remote server connection lost
Ref https://github.com/GNS3/gns3-gui/issues/2074
2017-05-22 13:57:32 +02:00
Julien Duponchelle 51d6152a5c Fix support for some old topologies
Fix #1042
2017-05-19 18:27:20 +02:00
Julien Duponchelle bdcadcc335 Fix support from some old topologies
Fix #1038
2017-05-19 17:49:39 +02:00
pyup.io bot 812ff6b5d8 Update sphinx from 1.5.6 to 1.6.1 (#1035) 2017-05-17 08:30:16 +02:00
Julien Duponchelle e24bfd5913 2.0.2dev1 2017-05-16 09:16:35 +02:00
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
Julien Duponchelle c88886911e 2.0.0 2017-05-02 10:16:11 +02:00
Bernhard Ehlers 1b0f5c63b8 Fix connection to websocket with last docker release
Fix https://github.com/GNS3/gns3-gui/issues/1994

Signed-off-by: Julien Duponchelle <julien@gns3.net>
2017-04-28 16:03:26 +02:00
Julien Duponchelle 2da581139c Lower docker requirements in tests also 2017-04-24 18:49:47 +02:00
Julien Duponchelle 1a90305baa Docker minimum api is 1.25 2017-04-24 18:43:12 +02:00
Julien Duponchelle effbe59414 Lock docker API to 1.27
Fix https://github.com/GNS3/gns3-gui/issues/1994
2017-04-24 17:51:55 +02:00
Julien Duponchelle 8b9f22c30c Handling server disconnect error when docker daemon die
Fix #985
2017-04-24 17:37:41 +02:00
Julien Duponchelle c9ceeee9de Handle some invalid SVG images
Fix #986
2017-04-24 17:31:20 +02:00
Julien Duponchelle 78e030b7ab 2.0.0dev13 2017-04-20 10:55:36 +02:00
Julien Duponchelle 456a28304d 2.0.0rc4 2017-04-20 10:52:55 +02:00
Julien Duponchelle 11321383c5 Remove not working trigger of nightly build 2017-04-19 15:53:39 +02:00
Julien Duponchelle d90a8fa482 Trigger nightly build when pushing 2017-04-19 15:52:36 +02:00
Julien Duponchelle 2962649dc8 Fix a race condition when handling error at project opening
Fix #983
2017-04-19 09:17:32 +02:00
Julien Duponchelle d9b93ccd66 Fix an issue with editing network on windows
Fix  #982
2017-04-18 18:04:00 +02:00
Julien Duponchelle 37e21f2a3d Skip a test for qemu not supported on windows 2017-04-18 16:35:36 +02:00
Julien Duponchelle c72ae1bfe7 Fix IOU test run on Windows 2017-04-18 16:21:45 +02:00
Julien Duponchelle 54014ccd39 Fix one more windows test 2017-04-18 16:01:07 +02:00
Julien Duponchelle b4434b5134 Add tests for checking if path are correctly cleaned 2017-04-18 15:50:11 +02:00
Julien Duponchelle 691c2dfd8e Drop a test about OVA no longer use 2017-04-18 14:27:44 +02:00
Julien Duponchelle 36de30e25e Fix various tests for windows 2017-04-18 14:23:29 +02:00
Julien Duponchelle 3de04d6e76 Install winpcap for the windows tests 2017-04-18 14:08:37 +02:00
Julien Duponchelle 51ef5d8c90 Fix a broken test on windows machines 2017-04-18 14:04:50 +02:00
Julien Duponchelle 0d687e62ad Try to enable test on appveyor 2017-04-18 13:54:30 +02:00
Julien Duponchelle 0bf6ed52cd Verbose test on windows 2017-04-18 13:39:34 +02:00
Julien Duponchelle b152360c5b Merge branch 'master' into 2.0 2017-04-18 12:33:19 +02:00
Julien Duponchelle 722028b2d2 Install win32 dependencies for the tests 2017-04-18 12:27:54 +02:00
Julien Duponchelle 25a992a870 Build only for Python 3.6 on Windows 2017-04-18 12:24:19 +02:00
Julien Duponchelle c50e80b2d3 Setup appveyor 2017-04-18 12:16:54 +02:00
Julien Duponchelle 5729d37992 Setup appveyor 2017-04-18 11:54:10 +02:00
Julien Duponchelle 7302f65ffe Catch timeout error on docker
Fix #981
2017-04-18 11:44:20 +02:00
Julien Duponchelle 152807e654 Merge branch 'master' into 2.0 2017-04-18 09:22:58 +02:00
Athmane Madjoudj 3518a781e9 typing is already included in Py >= 3.5 (#979) 2017-04-18 09:15:16 +02:00
Julien Duponchelle f3a87d76ef Fix import of some old topologies
Fix #977
2017-04-14 10:38:21 +02:00
Julien Duponchelle 57cdef6b02 Fix AttributeError: 'NoneType' object has no attribute 'returncode'
Fix #976
2017-04-14 10:33:19 +02:00
Julien Duponchelle d2d5f003f8 Fix a merge error 2017-04-14 09:09:43 +02:00
Julien Duponchelle 540ffdf5f3 Merge remote-tracking branch 'origin/master' into 2.0 2017-04-14 08:55:37 +02:00
Julien Duponchelle 5fb2a462c2 Merge master 2017-04-14 08:53:53 +02:00
Julien Duponchelle a9b76c9656 1.5.5dev1 2017-04-14 08:46:31 +02:00
Julien Duponchelle 838680cb40 1.5.4 2017-04-13 17:30:25 +02:00
Julien Duponchelle 85136bc2e4 Fix VPCS tests for recent version 2017-04-13 15:56:12 +02:00
Julien Duponchelle 6d150bb62c Freeze server dependencies to the same version used for 1.5.3 2017-04-13 14:45:27 +02:00
Bernhard Ehlers 3afe85ba3c Fix 1.5: Error message, when stopping IOU router #769
Signed-off-by: Julien Duponchelle <julien@gns3.net>
2017-04-13 14:25:40 +02:00
Julien Duponchelle 57ba7ab801 Fix crash in some tests 2017-04-13 14:24:26 +02:00
Julien Duponchelle b755840a27 Add security issues section 2017-04-13 12:38:16 +02:00
Julien Duponchelle a8294c3de8 Backport requirements of 2.0 2017-04-13 10:22:20 +02:00
Julien Duponchelle 878aee6e90 Correct version number 2017-04-13 09:55:22 +02:00
Julien Duponchelle 62cd5e4aa3 Fix ghost vmware vms
Fix #975
2017-04-11 15:05:31 +02:00
Julien Duponchelle 18835974fb Fix required field in schema not use
Fix #972
2017-04-11 10:57:01 +02:00
Julien Duponchelle ab2af5ceab Catch error and log them when we can't write the config
Fix #974
2017-04-10 17:44:09 +02:00
Julien Duponchelle fdd1084714 Fix bridge 'bridge0' already exist when we have trouble with a container
Fix #973
2017-04-10 16:58:00 +02:00
Julien Duponchelle 03a134af75 Catch an error at startup when the remote GNS3 VM is not a real GNS3 VM 2017-04-07 15:34:18 +02:00
grossmj 5edb631b00 Fixes Qemu sata option. Ref #875. 2017-04-07 17:25:14 +08:00
Julien Duponchelle ec6fdb7324 Catch GNS3 VM loading error at startup 2017-04-04 14:25:30 +02:00
pyup.io bot 7f621ed5dd Update sphinx from 1.5.4 to 1.5.5 (#967) 2017-04-03 23:44:26 +02:00
Julien Duponchelle 8f33d9ab79 Update documentation links
Fix #1977
2017-04-03 16:27:08 +02:00
Julien Duponchelle b7e1a56056 Catch a startup error due to the usage of some antivirus 2017-04-03 16:25:37 +02:00
pyup.io bot 11c7f35ea2 Update sphinx from 1.5.3 to 1.5.4 (#966) 2017-04-03 09:06:26 +02:00
Julien Duponchelle b92f881921 2.0.0dev12 2017-03-31 10:08:56 +02:00
68 changed files with 857 additions and 160 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
+80
View File
@@ -1,5 +1,85 @@
# Change Log
## 2.0.2 30/05/2017
* Set correct permission on ubridge when doing a remote installation
* Remote install script should be totally non interactive
* Duplicate project on remote server use UUID
* Fix import of some old topologies from 1.3
* Fix error in logging of error during starting GNS3 VM
* Fix an error when logging Docker container fail to start
* Use docker version in error message of outdated docker installation
* Support images created by "docker commit". Fixes #1039
* Do not wait auto start to finish in order to complete project opening
* Improve logging for remote server connection lost
## 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
* Lower docker requirements in tests also
* Docker minimum api is 1.25
* Handling server disconnect error when docker daemon die
* Handle some invalid SVG images
## 2.0.0rc4 20/04/2017
* Fix a race condition when handling error at project opening
* Fix an issue with editing network on windows
* Fix windows tests
* Catch timeout error on docker
* typing is already included in Py >= 3.5 (#979)
* Fix import of some old topologies
* Fix AttributeError: 'NoneType' object has no attribute 'returncode'
* Fix ghost vmware vms
* Fix required field in schema not use
* Catch error and log them when we can't write the config
* Fix bridge 'bridge0' already exist when we have trouble with a container
* Catch an error at startup when the remote GNS3 VM is not a real GNS3 VM
* Fixes Qemu sata option. Ref #875.
* Catch GNS3 VM loading error at startup
## 1.5.4 13/04/2017
* Fix VPCS tests for recent version
* Freeze server dependencies to the same version used for 1.5.3
* Fix 1.5: Error message, when stopping IOU router #769
* Drop color logging for remote install, seem to fail in some conditions
* Cleanup the remote install script
* Support for Xenial in remote install
## 2.0.0rc3 31/03/2017
* Support IOU image without .bin at the end
+5
View File
@@ -216,3 +216,8 @@ If you want test coverage:
.. code:: bash
py.test --cov-report term-missing --cov=gns3server
Security issues
----------------
Please contact us using contact informations available here:
http://docs.gns3.com/1ON9JBXSeR7Nt2-Qum2o3ZX0GU86BZwlmNSUgvmqNWGY/index.html
+21
View File
@@ -0,0 +1,21 @@
version: '{build}-{branch}'
image: Visual Studio 2015
platform: x64
environment:
PYTHON: "C:\\Python36-x64"
DISTUTILS_USE_SDK: "1"
API_TOKEN:
secure: VEKn4bYH3QO0ixtQW5ni4Enmn8cS1NlZV246ludBDgQ=
install:
- cinst nmap
- "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt"
- "%PYTHON%\\python.exe -m pip install -r win-requirements.txt"
build: off
test_script:
- "%PYTHON%\\python.exe -m pytest -v"
+2 -2
View File
@@ -1,7 +1,7 @@
-rrequirements.txt
sphinx==1.5.3
pytest==3.0.7
sphinx==1.6.2
pytest==3.1.0
pep8==1.7.0
pytest-catchlog==1.2.2
pytest-timeout==1.2.0
+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):
+16 -7
View File
@@ -20,10 +20,10 @@ Docker server module.
"""
import sys
import json
import asyncio
import logging
import aiohttp
import json
from gns3server.utils import parse_version
from gns3server.utils.asyncio import locked_coroutine
from gns3server.compute.base_manager import BaseManager
@@ -33,7 +33,9 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Err
log = logging.getLogger(__name__)
DOCKER_MINIMUM_API_VERSION = "1.21"
# Be carefull to keep it consistent
DOCKER_MINIMUM_API_VERSION = "1.25"
DOCKER_MINIMUM_VERSION = "1.13"
class Docker(BaseManager):
@@ -60,7 +62,7 @@ class Docker(BaseManager):
self._connected = False
raise DockerError("Can't connect to docker daemon")
if parse_version(version["ApiVersion"]) < parse_version(DOCKER_MINIMUM_API_VERSION):
raise DockerError("Docker API version is {}. GNS3 requires a minimum API version of {}".format(version["ApiVersion"], DOCKER_MINIMUM_API_VERSION))
raise DockerError("Docker version is {}. GNS3 requires a minimum version of {}".format(version["Version"], DOCKER_MINIMUM_VERSION))
def connector(self):
if self._connector is None or self._connector.closed:
@@ -113,11 +115,13 @@ class Docker(BaseManager):
:returns: HTTP response
"""
data = json.dumps(data)
url = "http://docker/" + path
if timeout is None:
timeout = 60 * 60 * 24 * 31 # One month timeout
if path == 'version':
url = "http://docker/v1.12/" + path # API of docker v1.0
else:
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
try:
if path != "version": # version is use by check connection
yield from self._check_connection()
@@ -134,6 +138,8 @@ class Docker(BaseManager):
)
except (aiohttp.ClientResponseError, aiohttp.ClientOSError) as e:
raise DockerError("Docker has returned an error: {}".format(str(e)))
except (asyncio.TimeoutError):
raise DockerError("Docker timeout " + method + " " + path)
if response.status >= 300:
body = yield from response.read()
try:
@@ -159,7 +165,7 @@ class Docker(BaseManager):
:returns: Websocket
"""
url = "http://docker/" + path
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
connection = yield from aiohttp.ws_connect(url,
connector=self.connector(),
origin="http://docker",
@@ -187,7 +193,10 @@ class Docker(BaseManager):
# The pull api will stream status via an HTTP JSON stream
content = ""
while True:
chunk = yield from response.content.read(1024)
try:
chunk = yield from response.content.read(1024)
except aiohttp.errors.ServerDisconnectedError:
break
if not chunk:
break
content += chunk.decode("utf-8")
+5 -2
View File
@@ -213,7 +213,7 @@ class DockerVM(BaseNode):
self._volumes = ["/etc/network"]
volumes = image_infos.get("ContainerConfig", {}).get("Volumes")
volumes = image_infos.get("Config", {}).get("Volumes")
if volumes is None:
return binds
for volume in volumes.keys():
@@ -361,6 +361,7 @@ class DockerVM(BaseNode):
try:
yield from self._add_ubridge_connection(nio, adapter_number)
except UbridgeNamespaceError:
log.error("Container %s failed to start", self.name)
yield from self.stop()
# The container can crash soon after the start, this means we can not move the interface to the container namespace
@@ -392,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)
@@ -517,6 +518,8 @@ class DockerVM(BaseNode):
state = yield from self._get_container_state()
if state == "running":
return True
if self.status == "started": # The container crashed we need to clean
yield from self.stop()
return False
@asyncio.coroutine
+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 = ''
+8 -4
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.")
@@ -1378,7 +1382,7 @@ class QemuVM(BaseNode):
# special case, sata controller doesn't exist in Qemu
options.extend(["-device", 'ahci,id=ahci{},bus=pci.{}'.format(disk_index, disk_index)])
options.extend(["-drive", 'file={},if=none,id=drive-sata-disk{},index={},media=disk'.format(disk, disk_index, disk_index)])
options.extend(["-device", 'ide-drive,drive=drive-sata-disk{},bus=ahci{}.0'.format(disk_index, disk_index)])
options.extend(["-device", 'ide-drive,drive=drive-sata-disk{},bus=ahci{}.0,id=drive-sata-disk{}'.format(disk_index, disk_index, disk_index)])
else:
options.extend(["-drive", 'file={},if={},index={},media=disk'.format(disk, interface, disk_index)])
+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()
+3 -2
View File
@@ -588,8 +588,9 @@ class VMware(BaseManager):
for vm_settings in vm_entries.values():
if "displayname" in vm_settings and "config" in vm_settings:
log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], vm_settings["config"]))
vmware_vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]})
if os.path.exists(vm_settings["config"]):
log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], vm_settings["config"]))
vmware_vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]})
return vmware_vms
def _get_vms_from_directory(self, directory):
+12 -6
View File
@@ -31,7 +31,7 @@ from .symbols import Symbols
from ..version import __version__
from .topology import load_topology
from .gns3vm import GNS3VM
from .gns3vm.gns3_vm_error import GNS3VMError
import logging
log = logging.getLogger(__name__)
@@ -87,10 +87,13 @@ 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()
yield from self.gns3vm.auto_start_vm()
try:
yield from self.gns3vm.auto_start_vm()
except GNS3VMError as e:
log.warn(str(e))
yield from self._project_auto_open()
def _update_config(self):
@@ -143,9 +146,12 @@ class Controller:
"password": c.password,
"compute_id": c.id
})
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
with open(self._config_file, 'w+') as f:
json.dump(data, f, indent=4)
try:
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
with open(self._config_file, 'w+') as f:
json.dump(data, f, indent=4)
except OSError as e:
log.error("Can't write the configuration {}: {}".format(self._config_file, str(e)))
@asyncio.coroutine
def _load_controller_settings(self):
+8 -2
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:
@@ -579,8 +580,10 @@ class Compute:
Forward a call to the emulator on compute
"""
try:
res = yield from self.http_query(method, "/{}/{}".format(type, path), data=data, timeout=None)
action = "/{}/{}".format(type, path)
res = yield from self.http_query(method, action, data=data, timeout=None)
except aiohttp.errors.DisconnectedError:
log.error("Connection lost to %s during %s %s", self._id, method, action)
raise aiohttp.web.HTTPGatewayTimeout()
return res.json
@@ -600,6 +603,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))
+10 -6
View File
@@ -18,6 +18,7 @@
import sys
import copy
import asyncio
import aiohttp
from ...utils.asyncio import locked_coroutine
from .vmware_gns3_vm import VMwareGNS3VM
@@ -242,11 +243,14 @@ class GNS3VM:
yield from self.start()
except GNS3VMError as e:
# User will receive the error later when they will try to use the node
yield from self._controller.add_compute(compute_id="vm",
name="GNS3 VM ({})".format(self.current_engine().vmname),
host=None,
force=True)
log.error("Can't start the GNS3 VM: {}", str(e))
try:
yield from self._controller.add_compute(compute_id="vm",
name="GNS3 VM ({})".format(self.current_engine().vmname),
host=None,
force=True)
except aiohttp.web.HTTPConflict:
pass
log.error("Can't start the GNS3 VM: %s", str(e))
@asyncio.coroutine
def exit_vm(self):
@@ -283,7 +287,7 @@ class GNS3VM:
yield from engine.start()
except Exception as e:
yield from self._controller.delete_compute("vm")
log.error("Can't start the GNS3 VM: {}", str(e))
log.error("Can't start the GNS3 VM: {}".format(str(e)))
yield from compute.update(name="GNS3 VM ({})".format(engine.vmname))
raise e
yield from compute.update(name="GNS3 VM ({})".format(engine.vmname),
@@ -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
+2 -3
View File
@@ -24,7 +24,6 @@ import asyncio
import zipfile
import aiohttp
from ..config import Config
from .topology import load_topology
@@ -74,7 +73,7 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
path = location
else:
projects_path = controller.projects_directory()
path = os.path.join(projects_path, project_name)
path = os.path.join(projects_path, project_id)
try:
os.makedirs(path, exist_ok=True)
except UnicodeEncodeError as e:
@@ -114,7 +113,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
+5 -2
View File
@@ -650,7 +650,7 @@ class Project:
self.dump()
# We catch all error to be able to rollback the .gns3 to the previous state
except Exception as e:
for compute in self._project_created_on_compute:
for compute in list(self._project_created_on_compute):
try:
yield from compute.post("/projects/{}/close".format(self._id))
# We don't care if a compute is down at this step
@@ -675,7 +675,10 @@ class Project:
self._loading = False
# Should we start the nodes when project is open
if self._auto_start:
yield from self.start_all()
# Start all in the background without waiting for completion
# we ignore errors because we want to let the user open
# their project and fix it
asyncio.async(self.start_all())
@asyncio.coroutine
def wait_loaded(self):
+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,
+24 -9
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
@@ -259,8 +265,11 @@ def _convert_1_3_later(topo, topo_path):
except KeyError:
node["compute_id"] = "local"
node["console_type"] = old_node["properties"].get("console_type", "telnet")
node["name"] = old_node["label"]["text"]
node["label"] = _convert_label(old_node["label"])
if "label" in old_node:
node["name"] = old_node["label"]["text"]
node["label"] = _convert_label(old_node["label"])
else:
node["name"] = old_node["properties"]["name"]
node["node_id"] = old_node.get("vm_id", str(uuid.uuid4()))
node["symbol"] = old_node.get("symbol", None)
@@ -280,6 +289,10 @@ def _convert_1_3_later(topo, topo_path):
node["properties"] = {}
# Some old dynamips node don't have type
if "type" not in old_node:
old_node["type"] = old_node["properties"]["platform"].upper()
if old_node["type"] == "VPCSDevice":
node["node_type"] = "vpcs"
elif old_node["type"] == "QemuVM":
@@ -297,7 +310,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
@@ -307,7 +320,7 @@ def _convert_1_3_later(topo, topo_path):
node["symbol"] = ":/symbols/ethernet_switch.svg"
node["console_type"] = None
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,
@@ -318,13 +331,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"].upper() 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"):
if "platform" not in node["properties"] and old_node["type"].upper().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://71c8e5474e304e2384abc4df1b245acb:9f6d1b1b885a4ad1895f55338d4522d7@sentry.io/38482"
DSN = "sync+https://67b93949a78d4ef5978388cc4b8906f9:271ee1dd01db4a39b919097f452cb6c5@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)
@@ -20,6 +20,7 @@ import aiohttp
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.utils import force_unix_path
from gns3server.schemas.node import (
NODE_OBJECT_SCHEMA,
@@ -337,7 +338,7 @@ class NodeHandler:
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
path = request.match_info["path"]
path = os.path.normpath(path)
path = force_unix_path(path)
# Raise error if user try to escape
if path[0] == ".":
@@ -23,7 +23,6 @@ import tempfile
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.controller.project import Project
from gns3server.controller.import_project import import_project
from gns3server.controller.export_project import export_project
from gns3server.config import Config
+230
View File
@@ -0,0 +1,230 @@
# -*- 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/>.
from aiohttp.web import HTTPConflict
from ...web.route import Route
from ...schemas.nio import NIO_SCHEMA
from ...schemas.vpcs import VPCS_CREATE_SCHEMA
from ...schemas.vpcs import VPCS_UPDATE_SCHEMA
from ...schemas.vpcs import VPCS_OBJECT_SCHEMA
from ...modules.vpcs import VPCS
class VPCSHandler:
"""
API entry points for VPCS.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/vms",
parameters={
"project_id": "UUID for the project"
},
status_codes={
201: "Instance created",
400: "Invalid request",
409: "Conflict"
},
description="Create a new VPCS instance",
input=VPCS_CREATE_SCHEMA,
output=VPCS_OBJECT_SCHEMA)
def create(request, response):
vpcs = VPCS.instance()
vm = yield from vpcs.create_vm(request.json["name"],
request.match_info["project_id"],
request.json.get("vm_id"),
console=request.json.get("console"),
startup_script=request.json.get("startup_script"))
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/vpcs/vms/{vm_id}",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance"
},
status_codes={
200: "Success",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Get a VPCS instance",
output=VPCS_OBJECT_SCHEMA)
def show(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/vpcs/vms/{vm_id}",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance"
},
status_codes={
200: "Instance updated",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Conflict"
},
description="Update a VPCS instance",
input=VPCS_UPDATE_SCHEMA,
output=VPCS_OBJECT_SCHEMA)
def update(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
vm.name = request.json.get("name", vm.name)
vm.console = request.json.get("console", vm.console)
vm.startup_script = request.json.get("startup_script", vm.startup_script)
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/vpcs/vms/{vm_id}",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance"
},
status_codes={
204: "Instance deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Delete a VPCS instance")
def delete(request, response):
yield from VPCS.instance().delete_vm(request.match_info["vm_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/vms/{vm_id}/start",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance"
},
status_codes={
204: "Instance started",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Start a VPCS instance",
output=VPCS_OBJECT_SCHEMA)
def start(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
yield from vm.start()
response.json(vm)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/vms/{vm_id}/stop",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance"
},
status_codes={
204: "Instance stopped",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Stop a VPCS instance")
def stop(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/vms/{vm_id}/reload",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance",
},
status_codes={
204: "Instance reloaded",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reload a VPCS instance")
def reload(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
yield from vm.reload()
response.set_status(204)
@Route.post(
r"/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port where the nio should be added"
},
status_codes={
201: "NIO created",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Add a NIO to a VPCS instance",
input=NIO_SCHEMA,
output=NIO_SCHEMA)
def create_nio(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
nio_type = request.json["type"]
if nio_type not in ("nio_udp", "nio_tap"):
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
nio = vpcs_manager.create_nio(vm.vpcs_path(), request.json)
vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port from where the nio should be removed"
},
status_codes={
204: "NIO deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Remove a NIO from a VPCS instance")
def delete_nio(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
vm.port_remove_nio_binding(int(request.match_info["port_number"]))
response.set_status(204)
+1 -1
View File
@@ -163,7 +163,7 @@ def pid_lock(path):
pid = int(f.read())
try:
os.kill(pid, 0) # If the proces is not running kill return an error
except OSError:
except (OSError, SystemError):
pid = None
except OSError as e:
log.critical("Can't open pid file %s: %s", pid, str(e))
+2 -1
View File
@@ -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 copy
ATM_SWITCH_CREATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
@@ -81,5 +82,5 @@ ATM_SWITCH_OBJECT_SCHEMA = {
"required": ["name", "node_id", "project_id"]
}
ATM_SWITCH_UPDATE_SCHEMA = ATM_SWITCH_OBJECT_SCHEMA
ATM_SWITCH_UPDATE_SCHEMA = copy.deepcopy(ATM_SWITCH_OBJECT_SCHEMA)
del ATM_SWITCH_UPDATE_SCHEMA["required"]
+2 -1
View File
@@ -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 copy
from .port import PORT_OBJECT_SCHEMA
HOST_INTERFACE_SCHEMA = {
@@ -136,5 +137,5 @@ CLOUD_OBJECT_SCHEMA = {
"required": ["name", "node_id", "project_id", "ports_mapping"]
}
CLOUD_UPDATE_SCHEMA = CLOUD_OBJECT_SCHEMA
CLOUD_UPDATE_SCHEMA = copy.deepcopy(CLOUD_OBJECT_SCHEMA)
del CLOUD_UPDATE_SCHEMA["required"]
+3 -2
View File
@@ -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 copy
from .capabilities import CAPABILITIES_SCHEMA
COMPUTE_CREATE_SCHEMA = {
@@ -52,10 +53,10 @@ COMPUTE_CREATE_SCHEMA = {
}
},
"additionalProperties": False,
"required": ["compute_id", "protocol", "host", "port"]
"required": ["protocol", "host", "port"]
}
COMPUTE_UPDATE_SCHEMA = COMPUTE_CREATE_SCHEMA
COMPUTE_UPDATE_SCHEMA = copy.deepcopy(COMPUTE_CREATE_SCHEMA)
del COMPUTE_UPDATE_SCHEMA["required"]
COMPUTE_OBJECT_SCHEMA = {
+1 -1
View File
@@ -33,7 +33,7 @@ VM_CREATE_SCHEMA = {
},
"dynamips_id": {
"description": "Dynamips ID",
"type": "integer"
"type": ["integer", "null"]
},
"name": {
"description": "Dynamips VM instance name",
+2 -1
View File
@@ -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 copy
ETHERNET_HUB_CREATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
@@ -129,5 +130,5 @@ ETHERNET_HUB_OBJECT_SCHEMA = {
"required": ["name", "node_id", "project_id", "ports_mapping"]
}
ETHERNET_HUB_UPDATE_SCHEMA = ETHERNET_HUB_OBJECT_SCHEMA
ETHERNET_HUB_UPDATE_SCHEMA = copy.deepcopy(ETHERNET_HUB_OBJECT_SCHEMA)
del ETHERNET_HUB_UPDATE_SCHEMA["required"]
+2 -1
View File
@@ -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 copy
ETHERNET_SWITCH_CREATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
@@ -153,5 +154,5 @@ ETHERNET_SWITCH_OBJECT_SCHEMA = {
"required": ["name", "node_id", "project_id"]
}
ETHERNET_SWITCH_UPDATE_SCHEMA = ETHERNET_SWITCH_OBJECT_SCHEMA
ETHERNET_SWITCH_UPDATE_SCHEMA = copy.deepcopy(ETHERNET_SWITCH_OBJECT_SCHEMA)
del ETHERNET_SWITCH_UPDATE_SCHEMA["required"]
+2 -1
View File
@@ -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 copy
FRAME_RELAY_SWITCH_CREATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
@@ -81,5 +82,5 @@ FRAME_RELAY_SWITCH_OBJECT_SCHEMA = {
"required": ["name", "node_id", "project_id"]
}
FRAME_RELAY_SWITCH_UPDATE_SCHEMA = FRAME_RELAY_SWITCH_OBJECT_SCHEMA
FRAME_RELAY_SWITCH_UPDATE_SCHEMA = copy.deepcopy(FRAME_RELAY_SWITCH_OBJECT_SCHEMA)
del FRAME_RELAY_SWITCH_UPDATE_SCHEMA["required"]
+2 -1
View File
@@ -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 copy
from .label import LABEL_OBJECT_SCHEMA
NODE_TYPE_SCHEMA = {
@@ -234,5 +235,5 @@ NODE_OBJECT_SCHEMA = {
}
NODE_CREATE_SCHEMA = NODE_OBJECT_SCHEMA
NODE_UPDATE_SCHEMA = NODE_OBJECT_SCHEMA
NODE_UPDATE_SCHEMA = copy.deepcopy(NODE_OBJECT_SCHEMA)
del NODE_UPDATE_SCHEMA["required"]
+2 -2
View File
@@ -5,7 +5,7 @@
</h3>
<ul>
<li><a href="https://gns3.com">Website</a></li>
<li><a href="http://api.gns3.net">API documentation</a></li>
<li><a href="http://docs.gns3.com">Documentation</a></li>
</ul>
<p>If you are looking for uploading the IOU. You can since 1.4 upload them directly from the client see: <a href="https://gns3.com/support/docs/how-to-configure-non-native-io-3">this documentation</a>.</p>
<p>If you are looking for uploading the IOU. You can since 1.4 upload them directly from the client see: <a href="http://docs.gns3.com/1PKfYwR78QP_Z3jqxBQ1pdy6SsqM27qhvdCvSmIizRh4">this documentation</a>.</p>
{% endblock %}
+1 -1
View File
@@ -199,7 +199,7 @@ class Hypervisor(UBridgeHypervisor):
try:
yield from wait_for_process_termination(self._process, timeout=3)
except asyncio.TimeoutError:
if self._process.returncode is None:
if self._process and self._process.returncode is None:
log.warn("uBridge process {} is still running... killing it".format(self._process.pid))
try:
self._process.kill()
+1 -1
View File
@@ -79,7 +79,7 @@ def list_images(type):
images.append({
"filename": filename,
"path": path,
"path": force_unix_path(path),
"md5sum": md5sum(os.path.join(root, filename)),
"filesize": os.stat(os.path.join(root, filename)).st_size})
except OSError as e:
+6 -2
View File
@@ -17,7 +17,7 @@
import io
import struct
from xml.etree.ElementTree import ElementTree
from xml.etree.ElementTree import ElementTree, ParseError
def get_size(data, default_width=0, default_height=0):
@@ -95,7 +95,11 @@ def get_size(data, default_width=0, default_height=0):
filetype = "svg"
fhandle = io.BytesIO(data)
tree = ElementTree()
tree.parse(fhandle)
try:
tree.parse(fhandle)
except ParseError:
raise ValueError("Invalid SVG file")
root = tree.getroot()
try:
+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.0rc3"
__version__ = "2.0.2"
# 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, -99)
__version_info__ = (2, 0, 2, 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 -1
View File
@@ -2,8 +2,8 @@ jsonschema>=2.4.0
aiohttp>=1.3.5,<=1.4.0 # pyup: ignore
aiohttp-cors==0.5.1 # pyup: ignore
yarl>=0.9.8,<0.10 # pyup: ignore
typing>=3.5.3.0 # Otherwise yarl fail with python 3.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
+5 -1
View File
@@ -152,7 +152,7 @@ log "Update system packages"
apt-get update
log "Upgrade packages"
apt-get upgrade -y
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
log " Install GNS3 packages"
apt-get install -y gns3-server
@@ -163,6 +163,10 @@ then
useradd -d /opt/gns3/ -m gns3
fi
log "Add GNS3 to the ubridge group"
usermod -aG ubridge gns3
log "Install docker"
if [ ! -f "/usr/bin/docker" ]
then
+3 -3
View File
@@ -47,7 +47,7 @@ def test_query_success(loop, vm):
vm._session.request = AsyncioMagicMock(return_value=response)
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
vm._session.request.assert_called_with('POST',
'http://docker/test',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
@@ -70,7 +70,7 @@ def test_query_error(loop, vm):
with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
vm._session.request.assert_called_with('POST',
'http://docker/test',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
@@ -91,7 +91,7 @@ def test_query_error_json(loop, vm):
with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
vm._session.request.assert_called_with('POST',
'http://docker/test',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
+2 -2
View File
@@ -840,7 +840,7 @@ def test_get_image_informations(project, manager, loop):
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_mount_binds(vm, tmpdir):
image_infos = {
"ContainerConfig": {
"Config": {
"Volumes": {
"/test/experimental": {}
}
@@ -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):
+1
View File
@@ -154,6 +154,7 @@ def test_termination_callback(vm, async_run):
assert event == vm
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_termination_callback_error(vm, tmpdir, async_run):
with open(str(tmpdir / "qemu.log"), "w+") as f:
+1 -13
View File
@@ -219,18 +219,6 @@ def test_get_relative_image_path(qemu, tmpdir, config):
assert qemu.get_relative_image_path(path5) == path5
def test_get_relative_image_path_ova(qemu, tmpdir, config):
os.makedirs(str(tmpdir / "QEMU" / "test.ova"))
path = str(tmpdir / "QEMU" / "test.ova" / "test.bin")
open(path, 'w+').close()
config.set_section_config("Server", {
"images_path": str(tmpdir)
})
assert qemu.get_relative_image_path(path) == os.path.join("test.ova", "test.bin")
assert qemu.get_relative_image_path(os.path.join("test.ova", "test.bin")) == os.path.join("test.ova", "test.bin")
def test_list_images(loop, qemu, tmpdir):
fake_images = ["a.qcow2", "b.qcow2", ".blu.qcow2", "a.qcow2.md5sum"]
@@ -262,7 +250,7 @@ def test_list_images_recursives(loop, qemu, tmpdir):
assert loop.run_until_complete(qemu.list_images()) == [
{"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
{"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
{"filename": "c.qcow2", "path": os.path.sep.join(["c", "c.qcow2"]), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}
{"filename": "c.qcow2", "path": force_unix_path(os.path.sep.join(["c", "c.qcow2"])), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}
]
+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):
+55
View File
@@ -35,6 +35,8 @@ def test_import_project(async_run, tmpdir, controller):
topology = {
"project_id": str(uuid.uuid4()),
"name": "test",
"auto_open": True,
"auto_start": True,
"topology": {
},
"version": "2.0.0"
@@ -67,6 +69,8 @@ def test_import_project(async_run, tmpdir, controller):
# A new project name is generated when you import twice the same name
with open(zip_path, "rb") as f:
project = async_run(import_project(controller, str(uuid.uuid4()), f))
assert project.auto_open is False
assert project.auto_start is False
assert project.name != "test"
@@ -181,6 +185,7 @@ def test_import_iou_linux_no_vm(linux_platform, async_run, tmpdir, controller):
{
"compute_id": "local",
"node_type": "iou",
"name": "test",
"properties": {}
}
],
@@ -224,6 +229,51 @@ def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller)
"compute_id": "local",
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
"node_type": "iou",
"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_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": {}
}
],
@@ -267,11 +317,13 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller):
"compute_id": "local",
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
"node_type": "iou",
"name": "test",
"properties": {}
},
{
"compute_id": "local",
"node_type": "vpcs",
"name": "test2",
"properties": {}
}
],
@@ -319,12 +371,14 @@ def test_import_node_id(linux_platform, async_run, tmpdir, controller):
"compute_id": "local",
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
"node_type": "iou",
"name": "test",
"properties": {}
},
{
"compute_id": "local",
"node_id": "c3ae286c-c81f-40d9-a2d0-5874b2f2478d",
"node_type": "iou",
"name": "test2",
"properties": {}
}
],
@@ -409,6 +463,7 @@ def test_import_keep_compute_id(windows_platform, async_run, tmpdir, controller)
"compute_id": "local",
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
"node_type": "iou",
"name": "test",
"properties": {}
}
],
+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"
}
+1 -5
View File
@@ -35,11 +35,7 @@ def test_symbols(http_controller):
def test_get(http_controller):
response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/firewall.svg') + '/raw')
assert response.status == 200
# Different carriage return
if sys.platform.startswith("win"):
assert response.headers['CONTENT-LENGTH'] == '9568'
else:
assert response.headers['CONTENT-LENGTH'] == '9381'
assert response.headers['CONTENT-LENGTH'] == '9381'
assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
assert '</svg>' in response.html
+2
View File
@@ -24,6 +24,8 @@ def test_force_unix_path():
assert force_unix_path("a\\b") == "a/b"
assert force_unix_path("a\\b\\..\\c") == "a/c"
assert force_unix_path("C:\Temp") == "C:/Temp"
assert force_unix_path(force_unix_path("C:\Temp")) == "C:/Temp"
assert force_unix_path("a//b") == "a/b"
def test_macaddress_to_int():
@@ -0,0 +1,77 @@
{
"auto_start": false,
"name": "1_3_dynamips",
"project_id": "ba5790e1-2f51-443e-a3cc-1a2eee132888",
"revision": 6,
"topology": {
"computes": [
{
"compute_id": "local",
"host": "127.0.0.1",
"name": "Local",
"port": 8000,
"protocol": "http"
}
],
"drawings": [],
"links": [],
"nodes": [
{
"symbol": ":/symbols/iosv_virl.svg",
"compute_id": "local",
"console": 2001,
"console_type": "telnet",
"label": {
"rotation": 0,
"style": "font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #000000;fill-opacity: 1.0;",
"text": "R1",
"x": 22,
"y": -25
},
"name": "R1",
"node_id": "0bce6ad5-c688-4d4d-a425-f21aaf3927e2",
"node_type": "dynamips",
"port_name_format": "Ethernet{0}",
"port_segment_size": 0,
"first_port_name": null,
"properties": {
"dynamips_id": 1,
"auto_delete_disks": true,
"clock_divisor": 4,
"disk0": 0,
"disk1": 0,
"exec_area": 64,
"idlemax": 500,
"idlesleep": 30,
"image": "c7200-adventerprisek9-mz.124-24.T8.image",
"mac_addr": "ca01.2f39.0000",
"midplane": "vxr",
"mmap": true,
"npe": "npe-400",
"nvram": 512,
"platform": "c7200",
"power_supplies": [
1,
1
],
"ram": 512,
"sensors": [
22,
22,
22,
22
],
"slot0": "C7200-IO-FE",
"sparsemem": true,
"startup_config": "configs/i1_startup-config.cfg",
"system_id": "FTX0945W0MY"
},
"x": -112,
"y": -100,
"z": 1
}
]
},
"type": "topology",
"version": "ANYSTR"
}
@@ -0,0 +1,80 @@
{
"auto_start": false,
"name": "1_3_dynamips",
"project_id": "ba5790e1-2f51-443e-a3cc-1a2eee132888",
"revision": 3,
"topology": {
"nodes": [
{
"default_symbol": ":/symbols/iosv_virl.normal.svg",
"description": "Router c7200",
"dynamips_id": 1,
"hover_symbol": ":/symbols/iosv_virl.selected.svg",
"id": 1,
"label": {
"color": "#000000",
"font": "TypeWriter,10,-1,5,75,0,0,0,0,0",
"text": "R1",
"x": 22.6171875,
"y": -25.0
},
"ports": [
{
"adapter_number": 0,
"id": 1,
"name": "FastEthernet0/0",
"port_number": 0
}
],
"properties": {
"auto_delete_disks": true,
"clock_divisor": 4,
"console": 2001,
"disk0": 0,
"disk1": 0,
"exec_area": 64,
"idlemax": 500,
"idlesleep": 30,
"image": "c7200-adventerprisek9-mz.124-24.T8.image",
"mac_addr": "ca01.2f39.0000",
"midplane": "vxr",
"mmap": true,
"name": "R1",
"npe": "npe-400",
"nvram": 512,
"platform": "c7200",
"power_supplies": [
1,
1
],
"ram": 512,
"sensors": [
22,
22,
22,
22
],
"slot0": "C7200-IO-FE",
"sparsemem": true,
"startup_config": "configs/i1_startup-config.cfg",
"system_id": "FTX0945W0MY"
},
"server_id": 1,
"vm_id": "0bce6ad5-c688-4d4d-a425-f21aaf3927e2",
"x": -112.0,
"y": -100.0
}
],
"servers": [
{
"cloud": false,
"host": "127.0.0.1",
"id": 1,
"local": true,
"port": 8000
}
]
},
"type": "topology",
"version": "1.3.13"
}
+5
View File
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import io
import types
import asyncio
import unittest.mock
@@ -69,6 +70,10 @@ class AsyncioMagicMock(unittest.mock.MagicMock):
"""
Magic mock returning coroutine
"""
try:
__class__ = types.CoroutineType
except AttributeError: # Not supported with Python 3.4
__class__ = types.GeneratorType
def __init__(self, return_value=None, return_values=None, **kwargs):
"""
+14 -11
View File
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
from unittest.mock import patch
@@ -105,9 +106,10 @@ def test_list_images(tmpdir):
path = tmpdir / "images2" / "test_invalid.image"
path.write(b'NOTANELF', ensure=True)
path3 = tmpdir / "images1" / "IOU" / "test3.bin"
path3.write(b'\x7fELF\x01\x02\x01', ensure=True)
path3 = force_unix_path(str(path3))
if sys.platform.startswith("linux"):
path3 = tmpdir / "images1" / "IOU" / "test3.bin"
path3.write(b'\x7fELF\x01\x02\x01', ensure=True)
path3 = force_unix_path(str(path3))
path4 = tmpdir / "images1" / "QEMU" / "test4.qcow2"
path4.write("1", ensure=True)
@@ -137,14 +139,15 @@ def test_list_images(tmpdir):
}
]
assert list_images("iou") == [
{
'filename': 'test3.bin',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': 'test3.bin'
}
]
if sys.platform.startswith("linux"):
assert list_images("iou") == [
{
'filename': 'test3.bin',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': 'test3.bin'
}
]
assert list_images("qemu") == [
{
+3
View File
@@ -0,0 +1,3 @@
-rrequirements.txt
pypiwin32 # pyup: ignore