mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 17:28:08 +00:00
Merge branch '2.0' into 2.1
This commit is contained in:
commit
273a711459
72
CHANGELOG
72
CHANGELOG
@ -1,5 +1,77 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 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
|
||||||
|
* Allow to change some properties of an already connected ethernet switch
|
||||||
|
* Ensure we start only one ubridge
|
||||||
|
* Catch some broken hostname for compute node
|
||||||
|
* Fix limit of 20 docker containers
|
||||||
|
* Fix race conditions in creation of Frame Relay Switch
|
||||||
|
* Fix conversion of project from 1.X with custom symbol for cloud
|
||||||
|
* Dissallow parallel pull of docker images
|
||||||
|
* Add a scripts for running current dev version on GNS3 VM
|
||||||
|
* Fix a crash with missing size in the svg files
|
||||||
|
* Fix an utf8 error in auth code
|
||||||
|
* Improve vmrun timeout message
|
||||||
|
* Support utf-8 characters in user and password for auth
|
||||||
|
* Handle password configuration change on remote servers
|
||||||
|
* Fix Bug when delete fake-running VMBox
|
||||||
|
* Fix Can't connect to compute local on some computers
|
||||||
|
* Add a modification uuid to settings returned by the server
|
||||||
|
* Check python version in setup.py only for install
|
||||||
|
* Fix Session is closed when listing docker images
|
||||||
|
* Cleanup docker source code
|
||||||
|
* Use aiohttp session for docker queries
|
||||||
|
* Escape special characters from SVG text
|
||||||
|
* Fix some port short name display issues
|
||||||
|
* Catch server disconnected errors from computes
|
||||||
|
* Generate a node uuid if the uuid is missing in the .gns3
|
||||||
|
* Ensure to dump project before exporting it
|
||||||
|
* Fix return code check for SIGSEGV of IOU images
|
||||||
|
* Prevent vmname change for VirtualBox linked clone
|
||||||
|
* Upgrade to aiohttp 1.3.5 to solve issue with big file
|
||||||
|
* Handle some invalid svg
|
||||||
|
* Try to fix some 1.3 topology with corrupted data
|
||||||
|
* Fix ComputeError: Can't connect to Main server
|
||||||
|
* Catch error when the server as trouble to access to itself
|
||||||
|
* Catch a timeout error in docker
|
||||||
|
* Lock yarl version because 0.10 is not compatible with aiohttp 1.3
|
||||||
|
* Raise error if image are not avaible on main server during export
|
||||||
|
* Fix a race condition when killing ubridge
|
||||||
|
* If your settings from 1.X are broken with skip them at import
|
||||||
|
* Catch a permission error on symbols
|
||||||
|
* Catch unicode error when you try to duplicate a project with invalid characters
|
||||||
|
* Catch error when you try to put an invalid server url
|
||||||
|
* Fix an error when handling ubridge errors
|
||||||
|
* Fix crash when handling an error in project creation
|
||||||
|
|
||||||
## 2.0.0rc2 10/03/2017
|
## 2.0.0rc2 10/03/2017
|
||||||
|
|
||||||
* Drop color logging for remote install, seem to fail in some conditions
|
* Drop color logging for remote install, seem to fail in some conditions
|
||||||
|
@ -216,3 +216,8 @@ If you want test coverage:
|
|||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
py.test --cov-report term-missing --cov=gns3server
|
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
appveyor.yml
Normal file
21
appveyor.yml
Normal 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"
|
@ -1,6 +1,6 @@
|
|||||||
-rrequirements.txt
|
-rrequirements.txt
|
||||||
|
|
||||||
sphinx==1.5.3
|
sphinx==1.5.5
|
||||||
pytest==3.0.7
|
pytest==3.0.7
|
||||||
pep8==1.7.0
|
pep8==1.7.0
|
||||||
pytest-catchlog==1.2.2
|
pytest-catchlog==1.2.2
|
||||||
|
@ -20,10 +20,10 @@ Docker server module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
|
||||||
from gns3server.utils import parse_version
|
from gns3server.utils import parse_version
|
||||||
from gns3server.utils.asyncio import locked_coroutine
|
from gns3server.utils.asyncio import locked_coroutine
|
||||||
from gns3server.compute.base_manager import BaseManager
|
from gns3server.compute.base_manager import BaseManager
|
||||||
@ -33,7 +33,7 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Err
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
DOCKER_MINIMUM_API_VERSION = "1.21"
|
DOCKER_MINIMUM_API_VERSION = "1.25"
|
||||||
|
|
||||||
|
|
||||||
class Docker(BaseManager):
|
class Docker(BaseManager):
|
||||||
@ -113,7 +113,7 @@ class Docker(BaseManager):
|
|||||||
:returns: HTTP response
|
:returns: HTTP response
|
||||||
"""
|
"""
|
||||||
data = json.dumps(data)
|
data = json.dumps(data)
|
||||||
url = "http://docker/" + path
|
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
|
||||||
|
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = 60 * 60 * 24 * 31 # One month timeout
|
timeout = 60 * 60 * 24 * 31 # One month timeout
|
||||||
@ -134,6 +134,8 @@ class Docker(BaseManager):
|
|||||||
)
|
)
|
||||||
except (aiohttp.ClientResponseError, aiohttp.ClientOSError) as e:
|
except (aiohttp.ClientResponseError, aiohttp.ClientOSError) as e:
|
||||||
raise DockerError("Docker has returned an error: {}".format(str(e)))
|
raise DockerError("Docker has returned an error: {}".format(str(e)))
|
||||||
|
except (asyncio.TimeoutError):
|
||||||
|
raise DockerError("Docker timeout " + method + " " + path)
|
||||||
if response.status >= 300:
|
if response.status >= 300:
|
||||||
body = yield from response.read()
|
body = yield from response.read()
|
||||||
try:
|
try:
|
||||||
@ -187,7 +189,10 @@ class Docker(BaseManager):
|
|||||||
# The pull api will stream status via an HTTP JSON stream
|
# The pull api will stream status via an HTTP JSON stream
|
||||||
content = ""
|
content = ""
|
||||||
while True:
|
while True:
|
||||||
|
try:
|
||||||
chunk = yield from response.content.read(1024)
|
chunk = yield from response.content.read(1024)
|
||||||
|
except aiohttp.errors.ServerDisconnectedError:
|
||||||
|
break
|
||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
content += chunk.decode("utf-8")
|
content += chunk.decode("utf-8")
|
||||||
|
@ -361,6 +361,7 @@ class DockerVM(BaseNode):
|
|||||||
try:
|
try:
|
||||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||||
except UbridgeNamespaceError:
|
except UbridgeNamespaceError:
|
||||||
|
log.error("Container {} failed to start", self.name)
|
||||||
yield from self.stop()
|
yield from self.stop()
|
||||||
|
|
||||||
# The container can crash soon after the start, this means we can not move the interface to the container namespace
|
# The container can crash soon after the start, this means we can not move the interface to the container namespace
|
||||||
@ -517,6 +518,8 @@ class DockerVM(BaseNode):
|
|||||||
state = yield from self._get_container_state()
|
state = yield from self._get_container_state()
|
||||||
if state == "running":
|
if state == "running":
|
||||||
return True
|
return True
|
||||||
|
if self.status == "started": # The container crashed we need to clean
|
||||||
|
yield from self.stop()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -1379,7 +1379,7 @@ class QemuVM(BaseNode):
|
|||||||
# special case, sata controller doesn't exist in Qemu
|
# special case, sata controller doesn't exist in Qemu
|
||||||
options.extend(["-device", 'ahci,id=ahci{},bus=pci.{}'.format(disk_index, disk_index)])
|
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(["-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:
|
else:
|
||||||
options.extend(["-drive", 'file={},if={},index={},media=disk'.format(disk, interface, disk_index)])
|
options.extend(["-drive", 'file={},if={},index={},media=disk'.format(disk, interface, disk_index)])
|
||||||
|
|
||||||
|
@ -588,6 +588,7 @@ class VMware(BaseManager):
|
|||||||
|
|
||||||
for vm_settings in vm_entries.values():
|
for vm_settings in vm_entries.values():
|
||||||
if "displayname" in vm_settings and "config" in vm_settings:
|
if "displayname" in vm_settings and "config" in vm_settings:
|
||||||
|
if os.path.exists(vm_settings["config"]):
|
||||||
log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], 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"]})
|
vmware_vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]})
|
||||||
return vmware_vms
|
return vmware_vms
|
||||||
|
@ -35,6 +35,7 @@ from ..version import __version__
|
|||||||
from .topology import load_topology
|
from .topology import load_topology
|
||||||
from .gns3vm import GNS3VM
|
from .gns3vm import GNS3VM
|
||||||
from ..utils.get_resource import get_resource
|
from ..utils.get_resource import get_resource
|
||||||
|
from .gns3vm.gns3_vm_error import GNS3VMError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -159,10 +160,13 @@ class Controller:
|
|||||||
for c in computes:
|
for c in computes:
|
||||||
try:
|
try:
|
||||||
yield from self.add_compute(**c)
|
yield from self.add_compute(**c)
|
||||||
except aiohttp.web_exceptions.HTTPConflict:
|
except (aiohttp.web_exceptions.HTTPConflict):
|
||||||
pass # Skip not available servers at loading
|
pass # Skip not available servers at loading
|
||||||
yield from self.load_projects()
|
yield from self.load_projects()
|
||||||
|
try:
|
||||||
yield from self.gns3vm.auto_start_vm()
|
yield from self.gns3vm.auto_start_vm()
|
||||||
|
except GNS3VMError as e:
|
||||||
|
log.warn(str(e))
|
||||||
yield from self._project_auto_open()
|
yield from self._project_auto_open()
|
||||||
|
|
||||||
def _update_config(self):
|
def _update_config(self):
|
||||||
@ -215,9 +219,12 @@ class Controller:
|
|||||||
"password": c.password,
|
"password": c.password,
|
||||||
"compute_id": c.id
|
"compute_id": c.id
|
||||||
})
|
})
|
||||||
|
try:
|
||||||
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
|
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
|
||||||
with open(self._config_file, 'w+') as f:
|
with open(self._config_file, 'w+') as f:
|
||||||
json.dump(data, f, indent=4)
|
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
|
@asyncio.coroutine
|
||||||
def _load_controller_settings(self):
|
def _load_controller_settings(self):
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
from ...utils.asyncio import locked_coroutine
|
from ...utils.asyncio import locked_coroutine
|
||||||
from .vmware_gns3_vm import VMwareGNS3VM
|
from .vmware_gns3_vm import VMwareGNS3VM
|
||||||
@ -242,10 +243,13 @@ class GNS3VM:
|
|||||||
yield from self.start()
|
yield from self.start()
|
||||||
except GNS3VMError as e:
|
except GNS3VMError as e:
|
||||||
# User will receive the error later when they will try to use the node
|
# User will receive the error later when they will try to use the node
|
||||||
|
try:
|
||||||
yield from self._controller.add_compute(compute_id="vm",
|
yield from self._controller.add_compute(compute_id="vm",
|
||||||
name="GNS3 VM ({})".format(self.current_engine().vmname),
|
name="GNS3 VM ({})".format(self.current_engine().vmname),
|
||||||
host=None,
|
host=None,
|
||||||
force=True)
|
force=True)
|
||||||
|
except aiohttp.web.HTTPConflict:
|
||||||
|
pass
|
||||||
log.error("Can't start the GNS3 VM: {}", str(e))
|
log.error("Can't start the GNS3 VM: {}", str(e))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -674,7 +674,7 @@ class Project:
|
|||||||
self.dump()
|
self.dump()
|
||||||
# We catch all error to be able to rollback the .gns3 to the previous state
|
# We catch all error to be able to rollback the .gns3 to the previous state
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
for compute in self._project_created_on_compute:
|
for compute in list(self._project_created_on_compute):
|
||||||
try:
|
try:
|
||||||
yield from compute.post("/projects/{}/close".format(self._id))
|
yield from compute.post("/projects/{}/close".format(self._id))
|
||||||
# We don't care if a compute is down at this step
|
# We don't care if a compute is down at this step
|
||||||
|
@ -291,8 +291,11 @@ def _convert_1_3_later(topo, topo_path):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
node["compute_id"] = "local"
|
node["compute_id"] = "local"
|
||||||
node["console_type"] = old_node["properties"].get("console_type", "telnet")
|
node["console_type"] = old_node["properties"].get("console_type", "telnet")
|
||||||
|
if "label" in old_node:
|
||||||
node["name"] = old_node["label"]["text"]
|
node["name"] = old_node["label"]["text"]
|
||||||
node["label"] = _convert_label(old_node["label"])
|
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["node_id"] = old_node.get("vm_id", str(uuid.uuid4()))
|
||||||
|
|
||||||
node["symbol"] = old_node.get("symbol", None)
|
node["symbol"] = old_node.get("symbol", None)
|
||||||
|
@ -54,7 +54,7 @@ class CrashReport:
|
|||||||
Report crash to a third party service
|
Report crash to a third party service
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DSN = "sync+https://7c028290d17b4035916285b304d42311:ddf752e704c7423cacab93f8e34f713c@sentry.io/38482"
|
DSN = "sync+https://19cca90b55874be5862caf9b507fbd7b:1c0897efd092467a874e89b2e4803b29@sentry.io/38482"
|
||||||
if hasattr(sys, "frozen"):
|
if hasattr(sys, "frozen"):
|
||||||
cacert = get_resource("cacert.pem")
|
cacert = get_resource("cacert.pem")
|
||||||
if cacert is not None and os.path.isfile(cacert):
|
if cacert is not None and os.path.isfile(cacert):
|
||||||
|
@ -20,6 +20,7 @@ import aiohttp
|
|||||||
|
|
||||||
from gns3server.web.route import Route
|
from gns3server.web.route import Route
|
||||||
from gns3server.controller import Controller
|
from gns3server.controller import Controller
|
||||||
|
from gns3server.utils import force_unix_path
|
||||||
|
|
||||||
from gns3server.schemas.node import (
|
from gns3server.schemas.node import (
|
||||||
NODE_OBJECT_SCHEMA,
|
NODE_OBJECT_SCHEMA,
|
||||||
@ -337,7 +338,7 @@ class NodeHandler:
|
|||||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||||
node = project.get_node(request.match_info["node_id"])
|
node = project.get_node(request.match_info["node_id"])
|
||||||
path = request.match_info["path"]
|
path = request.match_info["path"]
|
||||||
path = os.path.normpath(path)
|
path = force_unix_path(path)
|
||||||
|
|
||||||
# Raise error if user try to escape
|
# Raise error if user try to escape
|
||||||
if path[0] == ".":
|
if path[0] == ".":
|
||||||
|
230
gns3server/handlers/api/vpcs_handler.py
Normal file
230
gns3server/handlers/api/vpcs_handler.py
Normal 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)
|
@ -163,7 +163,7 @@ def pid_lock(path):
|
|||||||
pid = int(f.read())
|
pid = int(f.read())
|
||||||
try:
|
try:
|
||||||
os.kill(pid, 0) # If the proces is not running kill return an error
|
os.kill(pid, 0) # If the proces is not running kill return an error
|
||||||
except OSError:
|
except (OSError, SystemError):
|
||||||
pid = None
|
pid = None
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
log.critical("Can't open pid file %s: %s", pid, str(e))
|
log.critical("Can't open pid file %s: %s", pid, str(e))
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
ATM_SWITCH_CREATE_SCHEMA = {
|
ATM_SWITCH_CREATE_SCHEMA = {
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
@ -81,5 +82,5 @@ ATM_SWITCH_OBJECT_SCHEMA = {
|
|||||||
"required": ["name", "node_id", "project_id"]
|
"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"]
|
del ATM_SWITCH_UPDATE_SCHEMA["required"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
from .port import PORT_OBJECT_SCHEMA
|
from .port import PORT_OBJECT_SCHEMA
|
||||||
|
|
||||||
HOST_INTERFACE_SCHEMA = {
|
HOST_INTERFACE_SCHEMA = {
|
||||||
@ -136,5 +137,5 @@ CLOUD_OBJECT_SCHEMA = {
|
|||||||
"required": ["name", "node_id", "project_id", "ports_mapping"]
|
"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"]
|
del CLOUD_UPDATE_SCHEMA["required"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
from .capabilities import CAPABILITIES_SCHEMA
|
from .capabilities import CAPABILITIES_SCHEMA
|
||||||
|
|
||||||
COMPUTE_CREATE_SCHEMA = {
|
COMPUTE_CREATE_SCHEMA = {
|
||||||
@ -52,10 +53,10 @@ COMPUTE_CREATE_SCHEMA = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": False,
|
"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"]
|
del COMPUTE_UPDATE_SCHEMA["required"]
|
||||||
|
|
||||||
COMPUTE_OBJECT_SCHEMA = {
|
COMPUTE_OBJECT_SCHEMA = {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
ETHERNET_HUB_CREATE_SCHEMA = {
|
ETHERNET_HUB_CREATE_SCHEMA = {
|
||||||
"$schema": "http://json-schema.org/draft-04/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"]
|
"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"]
|
del ETHERNET_HUB_UPDATE_SCHEMA["required"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
ETHERNET_SWITCH_CREATE_SCHEMA = {
|
ETHERNET_SWITCH_CREATE_SCHEMA = {
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
@ -153,5 +154,5 @@ ETHERNET_SWITCH_OBJECT_SCHEMA = {
|
|||||||
"required": ["name", "node_id", "project_id"]
|
"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"]
|
del ETHERNET_SWITCH_UPDATE_SCHEMA["required"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
FRAME_RELAY_SWITCH_CREATE_SCHEMA = {
|
FRAME_RELAY_SWITCH_CREATE_SCHEMA = {
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
@ -81,5 +82,5 @@ FRAME_RELAY_SWITCH_OBJECT_SCHEMA = {
|
|||||||
"required": ["name", "node_id", "project_id"]
|
"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"]
|
del FRAME_RELAY_SWITCH_UPDATE_SCHEMA["required"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
from .label import LABEL_OBJECT_SCHEMA
|
from .label import LABEL_OBJECT_SCHEMA
|
||||||
|
|
||||||
NODE_TYPE_SCHEMA = {
|
NODE_TYPE_SCHEMA = {
|
||||||
@ -234,5 +235,5 @@ NODE_OBJECT_SCHEMA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NODE_CREATE_SCHEMA = 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"]
|
del NODE_UPDATE_SCHEMA["required"]
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://gns3.com">Website</a></li>
|
<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>
|
</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 %}
|
{% endblock %}
|
||||||
|
@ -199,7 +199,7 @@ class Hypervisor(UBridgeHypervisor):
|
|||||||
try:
|
try:
|
||||||
yield from wait_for_process_termination(self._process, timeout=3)
|
yield from wait_for_process_termination(self._process, timeout=3)
|
||||||
except asyncio.TimeoutError:
|
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))
|
log.warn("uBridge process {} is still running... killing it".format(self._process.pid))
|
||||||
try:
|
try:
|
||||||
self._process.kill()
|
self._process.kill()
|
||||||
|
@ -79,7 +79,7 @@ def list_images(type):
|
|||||||
|
|
||||||
images.append({
|
images.append({
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"path": path,
|
"path": force_unix_path(path),
|
||||||
"md5sum": md5sum(os.path.join(root, filename)),
|
"md5sum": md5sum(os.path.join(root, filename)),
|
||||||
"filesize": os.stat(os.path.join(root, filename)).st_size})
|
"filesize": os.stat(os.path.join(root, filename)).st_size})
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
import struct
|
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):
|
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"
|
filetype = "svg"
|
||||||
fhandle = io.BytesIO(data)
|
fhandle = io.BytesIO(data)
|
||||||
tree = ElementTree()
|
tree = ElementTree()
|
||||||
|
try:
|
||||||
tree.parse(fhandle)
|
tree.parse(fhandle)
|
||||||
|
except ParseError:
|
||||||
|
raise ValueError("Invalid SVG file")
|
||||||
|
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -2,7 +2,6 @@ jsonschema>=2.4.0
|
|||||||
aiohttp>=1.3.5,<=1.4.0 # pyup: ignore
|
aiohttp>=1.3.5,<=1.4.0 # pyup: ignore
|
||||||
aiohttp-cors==0.5.1 # pyup: ignore
|
aiohttp-cors==0.5.1 # pyup: ignore
|
||||||
yarl>=0.9.8,<0.10 # 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
|
Jinja2>=2.7.3
|
||||||
raven>=5.23.0
|
raven>=5.23.0
|
||||||
psutil>=3.0.0
|
psutil>=3.0.0
|
||||||
|
3
setup.py
3
setup.py
@ -40,6 +40,9 @@ class PyTest(TestCommand):
|
|||||||
|
|
||||||
dependencies = open("requirements.txt", "r").read().splitlines()
|
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(
|
setup(
|
||||||
name="gns3-server",
|
name="gns3-server",
|
||||||
version=__import__("gns3server").__version__,
|
version=__import__("gns3server").__version__,
|
||||||
|
@ -47,7 +47,7 @@ def test_query_success(loop, vm):
|
|||||||
vm._session.request = AsyncioMagicMock(return_value=response)
|
vm._session.request = AsyncioMagicMock(return_value=response)
|
||||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||||
vm._session.request.assert_called_with('POST',
|
vm._session.request.assert_called_with('POST',
|
||||||
'http://docker/test',
|
'http://docker/v1.25/test',
|
||||||
data='{"a": true}',
|
data='{"a": true}',
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
params={'b': 1},
|
params={'b': 1},
|
||||||
@ -70,7 +70,7 @@ def test_query_error(loop, vm):
|
|||||||
with pytest.raises(DockerError):
|
with pytest.raises(DockerError):
|
||||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||||
vm._session.request.assert_called_with('POST',
|
vm._session.request.assert_called_with('POST',
|
||||||
'http://docker/test',
|
'http://docker/v1.25/test',
|
||||||
data='{"a": true}',
|
data='{"a": true}',
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
params={'b': 1},
|
params={'b': 1},
|
||||||
@ -91,7 +91,7 @@ def test_query_error_json(loop, vm):
|
|||||||
with pytest.raises(DockerError):
|
with pytest.raises(DockerError):
|
||||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||||
vm._session.request.assert_called_with('POST',
|
vm._session.request.assert_called_with('POST',
|
||||||
'http://docker/test',
|
'http://docker/v1.25/test',
|
||||||
data='{"a": true}',
|
data='{"a": true}',
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
params={'b': 1},
|
params={'b': 1},
|
||||||
|
@ -154,6 +154,7 @@ def test_termination_callback(vm, async_run):
|
|||||||
assert event == vm
|
assert event == vm
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||||
def test_termination_callback_error(vm, tmpdir, async_run):
|
def test_termination_callback_error(vm, tmpdir, async_run):
|
||||||
|
|
||||||
with open(str(tmpdir / "qemu.log"), "w+") as f:
|
with open(str(tmpdir / "qemu.log"), "w+") as f:
|
||||||
|
@ -219,18 +219,6 @@ def test_get_relative_image_path(qemu, tmpdir, config):
|
|||||||
assert qemu.get_relative_image_path(path5) == path5
|
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):
|
def test_list_images(loop, qemu, tmpdir):
|
||||||
|
|
||||||
fake_images = ["a.qcow2", "b.qcow2", ".blu.qcow2", "a.qcow2.md5sum"]
|
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()) == [
|
assert loop.run_until_complete(qemu.list_images()) == [
|
||||||
{"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
|
{"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
|
||||||
{"filename": "b.qcow2", "path": "b.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}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ def test_import_iou_linux_no_vm(linux_platform, async_run, tmpdir, controller):
|
|||||||
{
|
{
|
||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -224,6 +225,7 @@ def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller)
|
|||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -267,11 +269,13 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller):
|
|||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_type": "vpcs",
|
"node_type": "vpcs",
|
||||||
|
"name": "test2",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -319,12 +323,14 @@ def test_import_node_id(linux_platform, async_run, tmpdir, controller):
|
|||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_id": "c3ae286c-c81f-40d9-a2d0-5874b2f2478d",
|
"node_id": "c3ae286c-c81f-40d9-a2d0-5874b2f2478d",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test2",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -409,6 +415,7 @@ def test_import_keep_compute_id(windows_platform, async_run, tmpdir, controller)
|
|||||||
"compute_id": "local",
|
"compute_id": "local",
|
||||||
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
"node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b",
|
||||||
"node_type": "iou",
|
"node_type": "iou",
|
||||||
|
"name": "test",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -35,10 +35,6 @@ def test_symbols(http_controller):
|
|||||||
def test_get(http_controller):
|
def test_get(http_controller):
|
||||||
response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/firewall.svg') + '/raw')
|
response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/firewall.svg') + '/raw')
|
||||||
assert response.status == 200
|
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 response.headers['CONTENT-TYPE'] == 'image/svg+xml'
|
||||||
assert '</svg>' in response.html
|
assert '</svg>' in response.html
|
||||||
|
@ -24,6 +24,8 @@ def test_force_unix_path():
|
|||||||
assert force_unix_path("a\\b") == "a/b"
|
assert force_unix_path("a\\b") == "a/b"
|
||||||
assert force_unix_path("a\\b\\..\\c") == "a/c"
|
assert force_unix_path("a\\b\\..\\c") == "a/c"
|
||||||
assert force_unix_path("C:\Temp") == "C:/Temp"
|
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():
|
def test_macaddress_to_int():
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
@ -105,6 +106,7 @@ def test_list_images(tmpdir):
|
|||||||
path = tmpdir / "images2" / "test_invalid.image"
|
path = tmpdir / "images2" / "test_invalid.image"
|
||||||
path.write(b'NOTANELF', ensure=True)
|
path.write(b'NOTANELF', ensure=True)
|
||||||
|
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
path3 = tmpdir / "images1" / "IOU" / "test3.bin"
|
path3 = tmpdir / "images1" / "IOU" / "test3.bin"
|
||||||
path3.write(b'\x7fELF\x01\x02\x01', ensure=True)
|
path3.write(b'\x7fELF\x01\x02\x01', ensure=True)
|
||||||
path3 = force_unix_path(str(path3))
|
path3 = force_unix_path(str(path3))
|
||||||
@ -137,6 +139,7 @@ def test_list_images(tmpdir):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
assert list_images("iou") == [
|
assert list_images("iou") == [
|
||||||
{
|
{
|
||||||
'filename': 'test3.bin',
|
'filename': 'test3.bin',
|
||||||
|
3
win-requirements.txt
Normal file
3
win-requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-rrequirements.txt
|
||||||
|
|
||||||
|
pypiwin32 # pyup: ignore
|
Loading…
Reference in New Issue
Block a user