mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-04 14:31:13 +02:00
Merge branch '2.0' into 2.1
This commit is contained in:
37 files changed
+435
-70
No files matched your search
@@ -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,7 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Err
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DOCKER_MINIMUM_API_VERSION = "1.21"
|
||||
DOCKER_MINIMUM_API_VERSION = "1.25"
|
||||
|
||||
|
||||
class Docker(BaseManager):
|
||||
@@ -113,7 +113,7 @@ class Docker(BaseManager):
|
||||
:returns: HTTP response
|
||||
"""
|
||||
data = json.dumps(data)
|
||||
url = "http://docker/" + path
|
||||
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
|
||||
|
||||
if timeout is None:
|
||||
timeout = 60 * 60 * 24 * 31 # One month timeout
|
||||
@@ -134,6 +134,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:
|
||||
@@ -187,7 +189,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")
|
||||
|
||||
@@ -361,6 +361,7 @@ class DockerVM(BaseNode):
|
||||
try:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
except UbridgeNamespaceError:
|
||||
log.error("Container {} 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
|
||||
@@ -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
|
||||
|
||||
@@ -1379,7 +1379,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)])
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -35,6 +35,7 @@ from ..version import __version__
|
||||
from .topology import load_topology
|
||||
from .gns3vm import GNS3VM
|
||||
from ..utils.get_resource import get_resource
|
||||
from .gns3vm.gns3_vm_error import GNS3VMError
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -159,10 +160,13 @@ class Controller:
|
||||
for c in computes:
|
||||
try:
|
||||
yield from self.add_compute(**c)
|
||||
except aiohttp.web_exceptions.HTTPConflict:
|
||||
except (aiohttp.web_exceptions.HTTPConflict):
|
||||
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):
|
||||
@@ -215,9 +219,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):
|
||||
|
||||
@@ -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,10 +243,13 @@ 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)
|
||||
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: {}", str(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
|
||||
@@ -674,7 +674,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
|
||||
|
||||
@@ -291,8 +291,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)
|
||||
|
||||
@@ -54,7 +54,7 @@ class CrashReport:
|
||||
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"):
|
||||
cacert = get_resource("cacert.pem")
|
||||
if cacert is not None and os.path.isfile(cacert):
|
||||
|
||||
@@ -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] == ".":
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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 %}
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user