Handlers: cleanup and fixes.

pull/565/head
grossmj 8 years ago
parent 885d93be02
commit f81d35cc29

@ -22,7 +22,6 @@ Docker server module.
import asyncio
import logging
import aiohttp
import urllib
import json
from gns3server.utils import parse_version

@ -155,7 +155,7 @@ class VirtualBox(BaseManager):
continue
@asyncio.coroutine
def list_images(self):
def list_vms(self):
"""
Gets VirtualBox VM list.
"""

@ -31,7 +31,7 @@ log = logging.getLogger(__name__)
class Controller:
"""The controller manage multiple gns3 computes"""
"""The controller manage multiple gns3 compute servers"""
def __init__(self):
self._computes = {}
@ -97,7 +97,7 @@ class Controller:
# We disallow to create from the outside the
if compute_id == 'local':
return self._createLocalCompute()
return self._create_local_compute()
if compute_id not in self._computes:
compute = Compute(compute_id=compute_id, controller=self, **kwargs)
@ -105,7 +105,7 @@ class Controller:
self.save()
return self._computes[compute_id]
def _createLocalCompute(self):
def _create_local_compute(self):
"""
Create the local compute node. It's the controller itself
"""
@ -139,7 +139,7 @@ class Controller:
raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id))
@asyncio.coroutine
def addProject(self, project_id=None, **kwargs):
def add_project(self, project_id=None, **kwargs):
"""
Create a project or return an existing project

@ -17,16 +17,15 @@
from aiohttp.web import HTTPForbidden
from ....web.route import Route
from ....config import Config
from gns3server.web.route import Route
from gns3server.config import Config
class ConfigHandler:
@classmethod
@Route.post(
r"/config/reload",
description="Check if version is the same as the server",
description="Reload the server configuration file",
status_codes={
201: "Config reload",
403: "Config reload refused"
@ -35,6 +34,6 @@ class ConfigHandler:
config = Config.instance()
if config.get_section_config("Server").getboolean("local", False) is False:
raise HTTPForbidden(text="You can only reload the configuration for a local server")
raise HTTPForbidden(text="The configuration can only be reloaded on a local server")
config.reload()
response.set_status(201)

@ -18,40 +18,26 @@
import os
from aiohttp.web import HTTPConflict
from ....web.route import Route
from ....compute.docker import Docker
from gns3server.web.route import Route
from gns3server.compute.docker import Docker
from gns3server.schemas.node import NODE_CAPTURE_SCHEMA
from gns3server.schemas.nio import NIO_SCHEMA
from ....schemas.docker import (
from gns3server.schemas.docker import (
DOCKER_CREATE_SCHEMA,
DOCKER_OBJECT_SCHEMA,
DOCKER_UPDATE_SCHEMA,
DOCKER_LIST_IMAGES_SCHEMA
)
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....schemas.nio import NIO_SCHEMA
class DockerHandler:
"""API entry points for Docker."""
"""API entry points for Docker containers."""
@classmethod
@Route.get(
r"/docker/images",
status_codes={
200: "Success",
},
output=DOCKER_LIST_IMAGES_SCHEMA,
description="Get all available Docker images")
def show(request, response):
docker_manager = Docker.instance()
images = yield from docker_manager.list_images()
response.json(images)
@classmethod
@Route.post(
r"/projects/{project_id}/docker/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -63,33 +49,32 @@ class DockerHandler:
output=DOCKER_OBJECT_SCHEMA)
def create(request, response):
docker_manager = Docker.instance()
vm = yield from docker_manager.create_node(request.json.pop("name"),
request.match_info["project_id"],
request.json.get("node_id"),
image=request.json.pop("image"),
start_command=request.json.get("start_command"),
environment=request.json.get("environment"),
adapters=request.json.get("adapters"),
console=request.json.get("console"),
console_type=request.json.get("console_type"),
console_resolution=request.json.get("console_resolution", "1024x768"),
console_http_port=request.json.get("console_http_port", 80),
console_http_path=request.json.get("console_http_path", "/"),
aux=request.json.get("aux"))
container = yield from docker_manager.create_node(request.json.pop("name"),
request.match_info["project_id"],
request.json.get("node_id"),
image=request.json.pop("image"),
start_command=request.json.get("start_command"),
environment=request.json.get("environment"),
adapters=request.json.get("adapters"),
console=request.json.get("console"),
console_type=request.json.get("console_type"),
console_resolution=request.json.get("console_resolution", "1024x768"),
console_http_port=request.json.get("console_http_port", 80),
console_http_path=request.json.get("console_http_path", "/"),
aux=request.json.get("aux"))
for name, value in request.json.items():
if name != "_node_id":
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
if name != "node_id":
if hasattr(container, name) and getattr(container, name) != value:
setattr(container, name, value)
response.set_status(201)
response.json(vm)
response.json(container)
@classmethod
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/start",
parameters={
"project_id": "UUID of the project",
"node_id": "ID of the container"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
@ -101,16 +86,15 @@ class DockerHandler:
output=DOCKER_OBJECT_SCHEMA)
def start(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.start()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.start()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/stop",
parameters={
"project_id": "UUID of the project",
"node_id": "ID of the container"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -122,16 +106,15 @@ class DockerHandler:
output=DOCKER_OBJECT_SCHEMA)
def stop(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.stop()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/reload",
parameters={
"project_id": "UUID of the project",
"node_id": "ID of the container"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance restarted",
@ -143,16 +126,15 @@ class DockerHandler:
output=DOCKER_OBJECT_SCHEMA)
def reload(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.restart()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.restart()
response.set_status(204)
@classmethod
@Route.delete(
r"/projects/{project_id}/docker/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "ID for the container",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance deleted",
@ -162,16 +144,15 @@ class DockerHandler:
description="Delete a Docker container")
def delete(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.delete()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.delete()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/suspend",
r"/projects/{project_id}/docker/nodes/{node_id}/pause",
parameters={
"project_id": "UUID of the project",
"node_id": "ID of the container"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance paused",
@ -181,17 +162,37 @@ class DockerHandler:
description="Pause a Docker container",
input=DOCKER_CREATE_SCHEMA,
output=DOCKER_OBJECT_SCHEMA)
def suspend(request, response):
def pause(request, response):
docker_manager = Docker.instance()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.pause()
response.set_status(204)
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/unpause",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance unpaused",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Unpause a Docker container",
input=DOCKER_CREATE_SCHEMA,
output=DOCKER_OBJECT_SCHEMA)
def unpause(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.pause()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.unpause()
response.set_status(204)
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "ID of the container",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter where the nio should be added",
"port_number": "Port on the adapter"
},
@ -205,21 +206,20 @@ class DockerHandler:
output=NIO_SCHEMA)
def create_nio(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
nio_type = request.json["type"]
if nio_type not in ("nio_udp"):
if nio_type != "nio_udp":
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
nio = docker_manager.create_nio(int(request.match_info["adapter_number"]), request.json)
yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
yield from container.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "ID of the container",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter where the nio should be added",
"port_number": "Port on the adapter"
},
@ -231,16 +231,15 @@ class DockerHandler:
description="Remove a NIO from a Docker container")
def delete_nio(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from container.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
response.set_status(204)
@classmethod
@Route.put(
r"/projects/{project_id}/docker/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -254,25 +253,25 @@ class DockerHandler:
def update(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_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.aux = request.json.get("aux", vm.aux)
vm.console_type = request.json.get("console_type", vm.console_type)
vm.console_resolution = request.json.get("console_resolution", vm.console_resolution)
vm.console_http_port = request.json.get("console_http_port", vm.console_http_port)
vm.console_http_path = request.json.get("console_http_path", vm.console_http_path)
vm.start_command = request.json.get("start_command", vm.start_command)
vm.environment = request.json.get("environment", vm.environment)
vm.adapters = request.json.get("adapters", vm.adapters)
yield from vm.update()
response.json(vm)
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
container.name = request.json.get("name", container.name)
container.console = request.json.get("console", container.console)
container.aux = request.json.get("aux", container.aux)
container.console_type = request.json.get("console_type", container.console_type)
container.console_resolution = request.json.get("console_resolution", container.console_resolution)
container.console_http_port = request.json.get("console_http_port", container.console_http_port)
container.console_http_path = request.json.get("console_http_path", container.console_http_path)
container.start_command = request.json.get("start_command", container.start_command)
container.environment = request.json.get("environment", container.environment)
container.adapters = request.json.get("adapters", container.adapters)
yield from container.update()
response.json(container)
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to start a packet capture",
"port_number": "Port on the adapter"
},
@ -282,25 +281,25 @@ class DockerHandler:
404: "Instance doesn't exist",
409: "Node not started"
},
description="Start a packet capture on a Docker VM instance",
description="Start a packet capture on a Docker container instance",
input=NODE_CAPTURE_SCHEMA)
def start_capture(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
adapter_number = int(request.match_info["adapter_number"])
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
pcap_file_path = os.path.join(container.project.capture_working_directory(), request.json["capture_file_name"])
if not vm.is_running():
raise HTTPConflict(text="Cannot capture traffic on a non started VM")
yield from vm.start_capture(adapter_number, pcap_file_path)
if not container.is_running():
raise HTTPConflict(text="Cannot capture traffic on a non started Docker container")
yield from container.start_capture(adapter_number, pcap_file_path)
response.json({"pcap_file_path": str(pcap_file_path)})
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to stop a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -308,17 +307,29 @@ class DockerHandler:
204: "Capture stopped",
400: "Invalid request",
404: "Instance doesn't exist",
409: "VM not started"
409: "Container not started"
},
description="Stop a packet capture on a IOU VM instance")
description="Stop a packet capture on a Docker container instance")
def stop_capture(request, response):
docker_manager = Docker.instance()
vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
if not vm.is_running():
raise HTTPConflict(text="Cannot capture traffic on a non started VM")
if not container.is_running():
raise HTTPConflict(text="Cannot capture traffic on a non started Docker container")
adapter_number = int(request.match_info["adapter_number"])
yield from vm.stop_capture(adapter_number)
yield from container.stop_capture(adapter_number)
response.set_status(204)
@Route.get(
r"/docker/images",
status_codes={
200: "Success",
},
output=DOCKER_LIST_IMAGES_SCHEMA,
description="Get all available Docker images")
def show(request, response):
docker_manager = Docker.instance()
images = yield from docker_manager.list_images()
response.json(images)

@ -17,13 +17,17 @@
import os
import asyncio
from ....web.route import Route
from ....schemas.dynamips_device import DEVICE_CREATE_SCHEMA
from ....schemas.dynamips_device import DEVICE_UPDATE_SCHEMA
from ....schemas.dynamips_device import DEVICE_OBJECT_SCHEMA
from ....schemas.dynamips_device import DEVICE_NIO_SCHEMA
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....compute.dynamips import Dynamips
from gns3server.web.route import Route
from gns3server.schemas.node import NODE_CAPTURE_SCHEMA
from gns3server.compute.dynamips import Dynamips
from gns3server.schemas.dynamips_device import (
DEVICE_CREATE_SCHEMA,
DEVICE_UPDATE_SCHEMA,
DEVICE_OBJECT_SCHEMA,
DEVICE_NIO_SCHEMA
)
class DynamipsDeviceHandler:
@ -32,11 +36,10 @@ class DynamipsDeviceHandler:
API entry points for Dynamips devices.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/devices",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -57,12 +60,11 @@ class DynamipsDeviceHandler:
response.set_status(201)
response.json(device)
@classmethod
@Route.get(
r"/projects/{project_id}/dynamips/devices/{device_id}",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance"
"project_id": "Project UUID",
"device_id": "Device UUID"
},
status_codes={
200: "Success",
@ -77,12 +79,11 @@ class DynamipsDeviceHandler:
device = dynamips_manager.get_device(request.match_info["device_id"], project_id=request.match_info["project_id"])
response.json(device)
@classmethod
@Route.put(
r"/projects/{project_id}/dynamips/devices/{device_id}",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance"
"project_id": "Project UUID",
"device_id": "Device UUID"
},
status_codes={
200: "Instance updated",
@ -107,12 +108,11 @@ class DynamipsDeviceHandler:
response.json(device)
@classmethod
@Route.delete(
r"/projects/{project_id}/dynamips/devices/{device_id}",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance"
"project_id": "Project UUID",
"device_id": "Device UUID"
},
status_codes={
204: "Instance deleted",
@ -129,8 +129,8 @@ class DynamipsDeviceHandler:
@Route.post(
r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance",
"project_id": "Project UUID",
"device_id": "Device UUID",
"port_number": "Port on the device"
},
status_codes={
@ -162,12 +162,11 @@ class DynamipsDeviceHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance",
"project_id": "Project UUID",
"device_id": "Device UUID",
"port_number": "Port on the device"
},
status_codes={
@ -188,8 +187,8 @@ class DynamipsDeviceHandler:
@Route.post(
r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance",
"project_id": "Project UUID",
"device_id": "Device UUID",
"port_number": "Port on the device"
},
status_codes={
@ -211,8 +210,8 @@ class DynamipsDeviceHandler:
@Route.post(
r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"device_id": "UUID for the instance",
"project_id": "Project UUID",
"device_id": "Device UUID",
"port_number": "Port on the device"
},
status_codes={

@ -19,17 +19,23 @@ import os
import sys
import base64
from ....web.route import Route
from ....schemas.nio import NIO_SCHEMA
from ....schemas.dynamips_vm import VM_CREATE_SCHEMA
from ....schemas.dynamips_vm import VM_UPDATE_SCHEMA
from ....schemas.dynamips_vm import VM_OBJECT_SCHEMA
from ....schemas.dynamips_vm import VM_CONFIGS_SCHEMA
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....schemas.node import NODE_LIST_IMAGES_SCHEMA
from ....compute.dynamips import Dynamips
from ....compute.dynamips.dynamips_error import DynamipsError
from ....compute.project_manager import ProjectManager
from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.dynamips_error import DynamipsError
from gns3server.compute.project_manager import ProjectManager
from gns3server.schemas.node import (
NODE_CAPTURE_SCHEMA,
NODE_LIST_IMAGES_SCHEMA,
)
from gns3server.schemas.dynamips_vm import (
VM_CREATE_SCHEMA,
VM_UPDATE_SCHEMA,
VM_OBJECT_SCHEMA,
VM_CONFIGS_SCHEMA
)
DEFAULT_CHASSIS = {
"c1700": "1720",
@ -44,11 +50,10 @@ class DynamipsVMHandler:
API entry points for Dynamips VMs.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -66,24 +71,23 @@ class DynamipsVMHandler:
if platform in DEFAULT_CHASSIS:
default_chassis = DEFAULT_CHASSIS[platform]
vm = yield from dynamips_manager.create_node(request.json.pop("name"),
request.match_info["project_id"],
request.json.get("node_id"),
request.json.get("dynamips_id"),
platform,
console=request.json.get("console"),
aux=request.json.get("aux"),
chassis=request.json.pop("chassis", default_chassis))
request.match_info["project_id"],
request.json.get("node_id"),
request.json.get("dynamips_id"),
platform,
console=request.json.get("console"),
aux=request.json.get("aux"),
chassis=request.json.pop("chassis", default_chassis))
yield from dynamips_manager.update_vm_settings(vm, request.json)
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/dynamips/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
@ -98,12 +102,11 @@ class DynamipsVMHandler:
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/dynamips/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -121,12 +124,11 @@ class DynamipsVMHandler:
yield from dynamips_manager.update_vm_settings(vm, request.json)
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/dynamips/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
@ -138,16 +140,14 @@ class DynamipsVMHandler:
# check the project_id exists
ProjectManager.instance().get_project(request.match_info["project_id"])
yield from Dynamips.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
@ -166,12 +166,11 @@ class DynamipsVMHandler:
yield from vm.start()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -186,12 +185,11 @@ class DynamipsVMHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/suspend",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance suspended",
@ -206,12 +204,11 @@ class DynamipsVMHandler:
yield from vm.suspend()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/resume",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance resumed",
@ -226,12 +223,11 @@ class DynamipsVMHandler:
yield from vm.resume()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance reloaded",
@ -249,8 +245,8 @@ class DynamipsVMHandler:
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter where the nio should be added",
"port_number": "Port on the adapter"
},
@ -273,12 +269,11 @@ class DynamipsVMHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter from where the nio should be removed",
"port_number": "Port on the adapter"
},
@ -301,8 +296,8 @@ class DynamipsVMHandler:
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to start a packet capture",
"port_number": "Port on the adapter"
},
@ -334,8 +329,8 @@ class DynamipsVMHandler:
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to stop a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -356,6 +351,10 @@ class DynamipsVMHandler:
@Route.get(
r"/projects/{project_id}/dynamips/nodes/{node_id}/configs",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
200: "Configs retrieved",
400: "Invalid request",
@ -375,7 +374,7 @@ class DynamipsVMHandler:
startup_config_content = base64.b64decode(startup_config_base64).decode("utf-8", errors='replace')
result["startup_config_content"] = startup_config_content
else:
# nvram doesn't contain anything if the router has not been started at least once
# The NVRAM doesn't contain anything if the router has not been started at least once
# in this case just use the startup-config file
if vm.startup_config:
startup_config_path = os.path.join(module_workdir, vm.startup_config)
@ -392,7 +391,7 @@ class DynamipsVMHandler:
private_config_content = base64.b64decode(private_config_base64).decode("utf-8", errors='replace')
result["private_config_content"] = private_config_content
else:
# nvram doesn't contain anything if the router has not been started at least once
# The NVRAM doesn't contain anything if the router has not been started at least once
# in this case just use the private-config file
if vm.private_config:
private_config_path = os.path.join(module_workdir, vm.private_config)
@ -410,6 +409,10 @@ class DynamipsVMHandler:
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/configs/save",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
200: "Configs saved",
400: "Invalid request",
@ -425,6 +428,10 @@ class DynamipsVMHandler:
@Route.get(
r"/projects/{project_id}/dynamips/nodes/{node_id}/idlepc_proposals",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
200: "Idle-PCs retrieved",
400: "Invalid request",
@ -442,6 +449,10 @@ class DynamipsVMHandler:
@Route.get(
r"/projects/{project_id}/dynamips/nodes/{node_id}/auto_idlepc",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
200: "Best Idle-pc value found",
400: "Invalid request",
@ -457,28 +468,31 @@ class DynamipsVMHandler:
response.json({"idlepc": idlepc})
@Route.get(
r"/dynamips/nodes",
r"/dynamips/images",
status_codes={
200: "List of Dynamips VM retrieved",
200: "List of Dynamips IOS images",
},
description="Retrieve the list of Dynamips VMS",
description="Retrieve the list of Dynamips IOS images",
output=NODE_LIST_IMAGES_SCHEMA)
def list_vms(request, response):
def list_images(request, response):
dynamips_manager = Dynamips.instance()
vms = yield from dynamips_manager.list_images()
images = yield from dynamips_manager.list_images()
response.set_status(200)
response.json(vms)
response.json(images)
@Route.post(
r"/dynamips/nodes/{path}",
r"/dynamips/images/{filename:.+}",
parameters={
"filename": "Image filename"
},
status_codes={
204: "Image uploaded",
204: "Upload a Dynamips IOS image",
},
raw=True,
description="Upload Dynamips image")
description="Upload a Dynamips IOS image")
def upload_image(request, response):
dynamips_manager = Dynamips.instance()
yield from dynamips_manager.write_image(request.match_info["path"], request.content)
yield from dynamips_manager.write_image(request.match_info["filename"], request.content)
response.set_status(204)

@ -18,16 +18,22 @@
import os
from aiohttp.web import HTTPConflict
from ....web.route import Route
from ....schemas.nio import NIO_SCHEMA
from ....schemas.iou import IOU_CREATE_SCHEMA
from ....schemas.iou import IOU_START_SCHEMA
from ....schemas.iou import IOU_UPDATE_SCHEMA
from ....schemas.iou import IOU_OBJECT_SCHEMA
from ....schemas.iou import IOU_CONFIGS_SCHEMA
from ....schemas.node import NODE_LIST_IMAGES_SCHEMA
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....compute.iou import IOU
from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.compute.iou import IOU
from gns3server.schemas.node import (
NODE_CAPTURE_SCHEMA,
NODE_LIST_IMAGES_SCHEMA,
)
from gns3server.schemas.iou import (
IOU_CREATE_SCHEMA,
IOU_START_SCHEMA,
IOU_UPDATE_SCHEMA,
IOU_OBJECT_SCHEMA,
IOU_CONFIGS_SCHEMA,
)
class IOUHandler:
@ -36,11 +42,10 @@ class IOUHandler:
API entry points for IOU.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/iou/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -72,19 +77,18 @@ class IOUHandler:
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/iou/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Get a IOU instance",
description="Get an IOU instance",
output=IOU_OBJECT_SCHEMA)
def show(request, response):
@ -92,12 +96,11 @@ class IOUHandler:
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/iou/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -105,7 +108,7 @@ class IOUHandler:
404: "Instance doesn't exist",
409: "Conflict"
},
description="Update a IOU instance",
description="Update an IOU instance",
input=IOU_UPDATE_SCHEMA,
output=IOU_OBJECT_SCHEMA)
def update(request, response):
@ -122,30 +125,28 @@ class IOUHandler:
vm.private_config = request.json.get("private_config_content")
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/iou/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Delete a IOU instance")
description="Delete an IOU instance")
def delete(request, response):
yield from IOU.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance started",
@ -154,7 +155,7 @@ class IOUHandler:
},
input=IOU_START_SCHEMA,
output=IOU_OBJECT_SCHEMA,
description="Start a IOU instance")
description="Start an IOU instance")
def start(request, response):
iou_manager = IOU.instance()
@ -167,19 +168,18 @@ class IOUHandler:
yield from vm.start()
response.json(vm)
@classmethod
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Stop a IOU instance")
description="Stop an IOU instance")
def stop(request, response):
iou_manager = IOU.instance()
@ -187,19 +187,18 @@ class IOUHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance reloaded",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reload a IOU instance")
description="Reload an IOU instance")
def reload(request, response):
iou_manager = IOU.instance()
@ -210,8 +209,8 @@ class IOUHandler:
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port where the nio should be added"
},
@ -235,12 +234,11 @@ class IOUHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port from where the nio should be removed"
},
@ -260,8 +258,8 @@ class IOUHandler:
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to start a packet capture",
"port_number": "Port on the adapter"
},
@ -271,7 +269,7 @@ class IOUHandler:
404: "Instance doesn't exist",
409: "VM not started"
},
description="Start a packet capture on a IOU VM instance",
description="Start a packet capture on an IOU VM instance",
input=NODE_CAPTURE_SCHEMA)
def start_capture(request, response):
@ -288,8 +286,8 @@ class IOUHandler:
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to stop a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -299,7 +297,7 @@ class IOUHandler:
404: "Instance doesn't exist",
409: "VM not started"
},
description="Stop a packet capture on a IOU VM instance")
description="Stop a packet capture on an IOU VM instance")
def stop_capture(request, response):
iou_manager = IOU.instance()
@ -315,6 +313,10 @@ class IOUHandler:
@Route.get(
r"/projects/{project_id}/iou/nodes/{node_id}/configs",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Configs retrieved",
400: "Invalid request",
@ -352,6 +354,10 @@ class IOUHandler:
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/configs/save",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Configs saved",
400: "Invalid request",
@ -366,28 +372,31 @@ class IOUHandler:
response.set_status(200)
@Route.get(
r"/iou/nodes",
r"/iou/images",
status_codes={
200: "List of IOU VM retrieved",
200: "List of IOU images",
},
description="Retrieve the list of IOU VMS",
description="Retrieve the list of IOU images",
output=NODE_LIST_IMAGES_SCHEMA)
def list_iou_nodes(request, response):
def list_iou_images(request, response):
iou_manager = IOU.instance()
iou_nodes = yield from iou_manager.list_images()
images = yield from iou_manager.list_images()
response.set_status(200)
response.json(iou_nodes)
response.json(images)
@Route.post(
r"/iou/nodes/{path}",
r"/iou/images/{filename:.+}",
parameters={
"filename": "Image filename"
},
status_codes={
204: "Image uploaded",
},
raw=True,
description="Upload IOU image.")
description="Upload an IOU image")
def upload_image(request, response):
iou_manager = IOU.instance()
yield from iou_manager.write_image(request.match_info["path"], request.content)
yield from iou_manager.write_image(request.match_info["filename"], request.content)
response.set_status(204)

@ -15,19 +15,18 @@
# 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 ....web.route import Route
from ....compute.port_manager import PortManager
from ....compute.project_manager import ProjectManager
from ....utils.interfaces import interfaces
from gns3server.web.route import Route
from gns3server.compute.port_manager import PortManager
from gns3server.compute.project_manager import ProjectManager
from gns3server.utils.interfaces import interfaces
class NetworkHandler:
@classmethod
@Route.post(
r"/projects/{project_id}/ports/udp",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
201: "UDP port allocated",
@ -43,7 +42,6 @@ class NetworkHandler:
response.set_status(201)
response.json({"udp_port": udp_port})
@classmethod
@Route.get(
r"/interfaces",
description="List all the network interfaces available on the server")

@ -17,17 +17,16 @@
import asyncio
from ....web.route import Route
from ....compute.notification_manager import NotificationManager
from aiohttp.web import WebSocketResponse
from gns3server.web.route import Route
from gns3server.compute.notification_manager import NotificationManager
class NotificationHandler:
@classmethod
@Route.get(
r"/notifications/ws",
description="Send notifications about what happend using websockets")
description="Send notifications using Websockets")
def notifications(request, response):
notifications = NotificationManager.instance()
ws = WebSocketResponse()
@ -36,8 +35,8 @@ class NotificationHandler:
with notifications.queue() as queue:
while True:
try:
notif = yield from queue.get_json(5)
except asyncio.futures.CancelledError as e:
notification = yield from queue.get_json(5)
except asyncio.futures.CancelledError:
break
ws.send_str(notif)
ws.send_str(notification)
return ws

@ -22,10 +22,17 @@ import os
import psutil
import tempfile
from ....web.route import Route
from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA, PROJECT_UPDATE_SCHEMA, PROJECT_FILE_LIST_SCHEMA, PROJECT_LIST_SCHEMA
from ....compute.project_manager import ProjectManager
from ....compute import MODULES
from gns3server.web.route import Route
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute import MODULES
from gns3server.schemas.project import (
PROJECT_OBJECT_SCHEMA,
PROJECT_CREATE_SCHEMA,
PROJECT_UPDATE_SCHEMA,
PROJECT_FILE_LIST_SCHEMA,
PROJECT_LIST_SCHEMA
)
import logging
log = logging.getLogger()
@ -33,13 +40,12 @@ log = logging.getLogger()
class ProjectHandler:
# How many clients has subcribe to notifications
# How many clients have subscribed to notifications
_notifications_listening = {}
@classmethod
@Route.get(
r"/projects",
description="List projects opened on the server",
description="List all projects opened on the server",
status_codes={
200: "Project list",
},
@ -51,13 +57,12 @@ class ProjectHandler:
response.set_status(200)
response.json(list(pm.projects))
@classmethod
@Route.post(
r"/projects",
description="Create a new project on the server",
status_codes={
201: "Project created",
403: "You are not allowed to modify this property",
403: "Forbidden to create a project",
409: "Project already created"
},
output=PROJECT_OBJECT_SCHEMA,
@ -74,12 +79,11 @@ class ProjectHandler:
response.set_status(201)
response.json(p)
@classmethod
@Route.get(
r"/projects/{project_id}",
description="Get project information",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "Success",
@ -92,16 +96,15 @@ class ProjectHandler:
project = pm.get_project(request.match_info["project_id"])
response.json(project)
@classmethod
@Route.put(
r"/projects/{project_id}",
description="Update a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "The project has been updated",
403: "You are not allowed to modify this property",
200: "Project updated",
403: "Forbidden to update this project",
404: "The project doesn't exist"
},
output=PROJECT_OBJECT_SCHEMA,
@ -118,16 +121,15 @@ class ProjectHandler:
for module in MODULES:
yield from module.instance().project_moved(project)
yield from project.clean_old_path(old_path)
# Very important we need to remove temporary flag after moving the project
# Very important: we need to remove temporary flag after moving the project
project.temporary = request.json.get("temporary", project.temporary)
response.json(project)
@classmethod
@Route.post(
r"/projects/{project_id}/commit",
description="Write changes on disk",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "Changes have been written on disk",
@ -140,15 +142,14 @@ class ProjectHandler:
yield from project.commit()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/close",
description="Close a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "The project has been closed",
204: "Project closed",
404: "The project doesn't exist"
})
def close(request, response):
@ -160,15 +161,14 @@ class ProjectHandler:
pm.remove_project(project.id)
del ProjectHandler._notifications_listening[project.id]
else:
log.warning("Skip project closing, another client is listening for project informations")
log.warning("Skip project closing, another client is listening for project notifications")
response.set_status(204)
@classmethod
@Route.delete(
r"/projects/{project_id}",
description="Delete a project from disk",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "Changes have been written on disk",
@ -182,12 +182,11 @@ class ProjectHandler:
pm.remove_project(project.id)
response.set_status(204)
@classmethod
@Route.get(
r"/projects/{project_id}/notifications",
description="Receive notifications about the projects",
description="Receive notifications about the project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "End of stream",
@ -201,7 +200,7 @@ class ProjectHandler:
response.content_type = "application/json"
response.set_status(200)
response.enable_chunked_encoding()
# Very important: do not send a content lenght otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
response.start(request)
@ -226,30 +225,27 @@ class ProjectHandler:
if project.id in ProjectHandler._notifications_listening:
ProjectHandler._notifications_listening[project.id] -= 1
@classmethod
def _getPingMessage(cls):
"""
The ping message is regulary send to the client to
keep the connection open. We send with it some informations
about server load.
Ping messages are regularly sent to the client to
keep the connection open. We send with it some information about server load.
:returns: hash
"""
stats = {}
# Non blocking call in order to get cpu usage. First call will return 0
# Non blocking call in order to get cpu usage. First call will return 0.
stats["cpu_usage_percent"] = psutil.cpu_percent(interval=None)
stats["memory_usage_percent"] = psutil.virtual_memory().percent
return {"action": "ping", "event": stats}
@classmethod
@Route.get(
r"/projects/{project_id}/files",
description="List files of a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "Return list of files",
200: "Return a list of files",
404: "The project doesn't exist"
},
output=PROJECT_FILE_LIST_SCHEMA)
@ -261,15 +257,14 @@ class ProjectHandler:
response.json(files)
response.set_status(200)
@classmethod
@Route.get(
r"/projects/{project_id}/files/{path:.+}",
description="Get a file of a project",
description="Get a file from a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "Return the file",
200: "File returned",
403: "Permission denied",
404: "The file doesn't exist"
})
@ -280,7 +275,7 @@ class ProjectHandler:
path = request.match_info["path"]
path = os.path.normpath(path)
# Raise error if user try to escape
# Raise an error if user try to escape
if path[0] == ".":
raise aiohttp.web.HTTPForbidden
path = os.path.join(project.path, path)
@ -288,7 +283,7 @@ class ProjectHandler:
response.content_type = "application/octet-stream"
response.set_status(200)
response.enable_chunked_encoding()
# Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
try:
@ -305,15 +300,14 @@ class ProjectHandler:
except PermissionError:
raise aiohttp.web.HTTPForbidden()
@classmethod
@Route.get(
r"/projects/{project_id}/stream/{path:.+}",
description="Stream a file from a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "Return the file",
200: "File returned",
403: "Permission denied",
404: "The file doesn't exist"
})
@ -324,7 +318,7 @@ class ProjectHandler:
path = request.match_info["path"]
path = os.path.normpath(path)
# Raise error if user try to escape
# Raise an error if user try to escape
if path[0] == ".":
raise aiohttp.web.HTTPForbidden
path = os.path.join(project.path, path)
@ -332,7 +326,7 @@ class ProjectHandler:
response.content_type = "application/octet-stream"
response.set_status(200)
response.enable_chunked_encoding()
# Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
try:
@ -349,16 +343,15 @@ class ProjectHandler:
except PermissionError:
raise aiohttp.web.HTTPForbidden()
@classmethod
@Route.post(
r"/projects/{project_id}/files/{path:.+}",
description="Get a file of a project",
description="Write a file to a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
raw=True,
status_codes={
200: "Return the file",
200: "File returned",
403: "Permission denied",
404: "The path doesn't exist"
})
@ -389,16 +382,15 @@ class ProjectHandler:
except PermissionError:
raise aiohttp.web.HTTPForbidden()
@classmethod
@Route.get(
r"/projects/{project_id}/export",
description="Export a project as a portable archive",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
raw=True,
status_codes={
200: "Return the file",
200: "File returned",
404: "The project doesn't exist"
})
def export_project(request, response):
@ -408,7 +400,7 @@ class ProjectHandler:
response.content_type = 'application/gns3project'
response.headers['CONTENT-DISPOSITION'] = 'attachment; filename="{}.gns3project"'.format(project.name)
response.enable_chunked_encoding()
# Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
response.start(request)
@ -418,18 +410,17 @@ class ProjectHandler:
yield from response.write_eof()
@classmethod
@Route.post(
r"/projects/{project_id}/import",
description="Import a project from a portable archive",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
raw=True,
output=PROJECT_OBJECT_SCHEMA,
status_codes={
200: "Project imported",
403: "You are not allowed to modify this property"
403: "Forbidden to import project"
})
def import_project(request, response):
@ -437,11 +428,9 @@ class ProjectHandler:
project_id = request.match_info["project_id"]
project = pm.create_project(project_id=project_id)
# We write the content to a temporary location
# and after extract all. It could be more optimal to stream
# this but it's not implemented in Python.
# 
# Spooled mean the file is temporary keep in ram until max_size
# We write the content to a temporary location and after we extract it all.
# It could be more optimal to stream this but it is not implemented in Python.
# Spooled means the file is temporary kept in memory until max_size is reached
try:
with tempfile.SpooledTemporaryFile(max_size=10000) as temp:
while True:

@ -19,19 +19,23 @@ import sys
import os.path
from aiohttp.web import HTTPConflict
from ....web.route import Route
from ....compute.project_manager import ProjectManager
from ....schemas.nio import NIO_SCHEMA
from ....schemas.qemu import QEMU_CREATE_SCHEMA
from ....schemas.qemu import QEMU_UPDATE_SCHEMA
from ....schemas.qemu import QEMU_OBJECT_SCHEMA
from ....schemas.qemu import QEMU_BINARY_FILTER_SCHEMA
from ....schemas.qemu import QEMU_BINARY_LIST_SCHEMA
from ....schemas.qemu import QEMU_CAPABILITY_LIST_SCHEMA
from ....schemas.qemu import QEMU_IMAGE_CREATE_SCHEMA
from ....schemas.node import NODE_LIST_IMAGES_SCHEMA
from ....compute.qemu import Qemu
from ....config import Config
from gns3server.web.route import Route
from gns3server.compute.project_manager import ProjectManager
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.schemas.node import NODE_LIST_IMAGES_SCHEMA
from gns3server.compute.qemu import Qemu
from gns3server.config import Config
from gns3server.schemas.qemu import (
QEMU_CREATE_SCHEMA,
QEMU_UPDATE_SCHEMA,
QEMU_OBJECT_SCHEMA,
QEMU_BINARY_LIST_SCHEMA,
QEMU_BINARY_FILTER_SCHEMA,
QEMU_CAPABILITY_LIST_SCHEMA,
QEMU_IMAGE_CREATE_SCHEMA
)
class QEMUHandler:
@ -40,11 +44,10 @@ class QEMUHandler:
API entry points for QEMU.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -73,12 +76,11 @@ class QEMUHandler:
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/qemu/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
@ -93,12 +95,11 @@ class QEMUHandler:
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/qemu/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -120,12 +121,11 @@ class QEMUHandler:
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/qemu/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
@ -138,12 +138,11 @@ class QEMUHandler:
yield from Qemu.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance started",
@ -156,20 +155,18 @@ class QEMUHandler:
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
if sys.platform.startswith("linux") and qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) \
and "-no-kvm" not in vm.options:
if sys.platform.startswith("linux") and qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) and "-no-kvm" not in vm.options:
pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False:
raise HTTPConflict(text="Cannot start VM with KVM enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
yield from vm.start()
response.json(vm)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"vm_node": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -184,12 +181,11 @@ class QEMUHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance reloaded",
@ -204,12 +200,11 @@ class QEMUHandler:
yield from vm.reload()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/suspend",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance suspended",
@ -224,12 +219,11 @@ class QEMUHandler:
yield from vm.suspend()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/resume",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance resumed",
@ -247,8 +241,8 @@ class QEMUHandler:
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port on the adapter (always 0)"
},
@ -272,12 +266,11 @@ class QEMUHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port on the adapter (always 0)"
},
@ -294,7 +287,6 @@ class QEMUHandler:
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
response.set_status(204)
@classmethod
@Route.get(
r"/qemu/binaries",
status_codes={
@ -310,7 +302,6 @@ class QEMUHandler:
binaries = yield from Qemu.binary_list(request.json.get("archs", None))
response.json(binaries)
@classmethod
@Route.get(
r"/qemu/img-binaries",
status_codes={
@ -340,7 +331,6 @@ class QEMUHandler:
capabilities["kvm"] = kvms
response.json(capabilities)
@classmethod
@Route.post(
r"/qemu/img",
status_codes={
@ -363,28 +353,31 @@ class QEMUHandler:
response.set_status(201)
@Route.get(
r"/qemu/nodes",
r"/qemu/images",
status_codes={
200: "List of Qemu images retrieved",
200: "List of Qemu images",
},
description="Retrieve the list of Qemu images",
output=NODE_LIST_IMAGES_SCHEMA)
def list_vm_nodes(request, response):
def list_qemu_images(request, response):
qemu_manager = Qemu.instance()
vm_nodes = yield from qemu_manager.list_images()
images = yield from qemu_manager.list_images()
response.set_status(200)
response.json(vm_nodes)
response.json(images)
@Route.post(
r"/qemu/nodes/{path:.+}",
r"/qemu/images/{filename:.+}",
parameters={
"filename": "Image filename"
},
status_codes={
204: "Image uploaded",
},
raw=True,
description="Upload Qemu image.")
description="Upload Qemu image")
def upload_image(request, response):
qemu_manager = Qemu.instance()
yield from qemu_manager.write_image(request.match_info["path"], request.content)
yield from qemu_manager.write_image(request.match_info["filename"], request.content)
response.set_status(204)

@ -15,16 +15,15 @@
# 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 ....web.route import Route
from ....config import Config
from ....schemas.version import VERSION_SCHEMA
from ....version import __version__
from gns3server.web.route import Route
from gns3server.config import Config
from gns3server.schemas.version import VERSION_SCHEMA
from gns3server.version import __version__
from aiohttp.web import HTTPConflict
class VersionHandler:
@classmethod
@Route.get(
r"/version",
description="Retrieve the server version number",
@ -35,7 +34,6 @@ class VersionHandler:
local_server = config.get_section_config("Server").getboolean("local", False)
response.json({"version": __version__, "local": local_server})
@classmethod
@Route.post(
r"/version",
description="Check if version is the same as the server",

@ -18,15 +18,17 @@
import os
from aiohttp.web import HTTPConflict
from ....web.route import Route
from ....schemas.nio import NIO_SCHEMA
from ....schemas.virtualbox import VBOX_CREATE_SCHEMA
from ....schemas.virtualbox import VBOX_UPDATE_SCHEMA
from ....schemas.virtualbox import VBOX_OBJECT_SCHEMA
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....compute.virtualbox import VirtualBox
from ....compute.project_manager import ProjectManager
from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.schemas.node import NODE_CAPTURE_SCHEMA
from gns3server.compute.virtualbox import VirtualBox
from gns3server.compute.project_manager import ProjectManager
from gns3server.schemas.virtualbox import (
VBOX_CREATE_SCHEMA,
VBOX_UPDATE_SCHEMA,
VBOX_OBJECT_SCHEMA
)
class VirtualBoxHandler:
@ -34,24 +36,10 @@ class VirtualBoxHandler:
API entry points for VirtualBox.
"""
@classmethod
@Route.get(
r"/virtualbox/vms",
status_codes={
200: "Success",
},
description="Get all available VirtualBox VMs")
def index(request, response):
vbox_manager = VirtualBox.instance()
vms = yield from vbox_manager.list_images()
response.json(vms)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -88,12 +76,11 @@ class VirtualBoxHandler:
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/virtualbox/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
@ -108,12 +95,11 @@ class VirtualBoxHandler:
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/virtualbox/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -153,12 +139,11 @@ class VirtualBoxHandler:
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/virtualbox/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
@ -170,16 +155,14 @@ class VirtualBoxHandler:
# check the project_id exists
ProjectManager.instance().get_project(request.match_info["project_id"])
yield from VirtualBox.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
@ -198,12 +181,11 @@ class VirtualBoxHandler:
yield from vm.start()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -218,12 +200,11 @@ class VirtualBoxHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/suspend",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance suspended",
@ -238,12 +219,11 @@ class VirtualBoxHandler:
yield from vm.suspend()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/resume",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance resumed",
@ -258,12 +238,11 @@ class VirtualBoxHandler:
yield from vm.resume()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance reloaded",
@ -281,8 +260,8 @@ class VirtualBoxHandler:
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter where the nio should be added",
"port_number": "Port on the adapter (always 0)"
},
@ -306,12 +285,11 @@ class VirtualBoxHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter from where the nio should be removed",
"port_number": "Port on the adapter (always 0)"
},
@ -331,8 +309,8 @@ class VirtualBoxHandler:
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to start a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -355,8 +333,8 @@ class VirtualBoxHandler:
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to stop a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -372,3 +350,14 @@ class VirtualBoxHandler:
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
vm.stop_capture(int(request.match_info["adapter_number"]))
response.set_status(204)
@Route.get(
r"/virtualbox/vms",
status_codes={
200: "Success",
},
description="Get all available VirtualBox VMs")
def get_vms(request, response):
vbox_manager = VirtualBox.instance()
vms = yield from vbox_manager.list_vms()
response.json(vms)

@ -18,14 +18,17 @@
import os
from aiohttp.web import HTTPConflict
from ....web.route import Route
from ....schemas.vmware import VMWARE_CREATE_SCHEMA
from ....schemas.vmware import VMWARE_UPDATE_SCHEMA
from ....schemas.vmware import VMWARE_OBJECT_SCHEMA
from ....schemas.node import NODE_CAPTURE_SCHEMA
from ....schemas.nio import NIO_SCHEMA
from ....compute.vmware import VMware
from ....compute.project_manager import ProjectManager
from gns3server.web.route import Route
from gns3server.schemas.node import NODE_CAPTURE_SCHEMA
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.compute.vmware import VMware
from gns3server.compute.project_manager import ProjectManager
from gns3server.schemas.vmware import (
VMWARE_CREATE_SCHEMA,
VMWARE_UPDATE_SCHEMA,
VMWARE_OBJECT_SCHEMA
)
class VMwareHandler:
@ -34,24 +37,10 @@ class VMwareHandler:
API entry points for VMware.
"""
@classmethod
@Route.get(
r"/vmware/vms",
status_codes={
200: "Success",
},
description="Get all VMware VMs available")
def index(request, response):
vmware_manager = VMware.instance()
vms = yield from vmware_manager.list_vms()
response.json(vms)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -79,12 +68,11 @@ class VMwareHandler:
response.set_status(201)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/vmware/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
@ -99,12 +87,11 @@ class VMwareHandler:
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/vmware/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -126,12 +113,11 @@ class VMwareHandler:
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/vmware/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
@ -146,12 +132,11 @@ class VMwareHandler:
yield from VMware.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
@ -170,12 +155,11 @@ class VMwareHandler:
yield from vm.start()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -190,12 +174,11 @@ class VMwareHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/suspend",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance suspended",
@ -210,12 +193,11 @@ class VMwareHandler:
yield from vm.suspend()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/resume",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance resumed",
@ -230,12 +212,11 @@ class VMwareHandler:
yield from vm.resume()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance reloaded",
@ -253,8 +234,8 @@ class VMwareHandler:
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter where the nio should be added",
"port_number": "Port on the adapter (always 0)"
},
@ -278,12 +259,11 @@ class VMwareHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter from where the nio should be removed",
"port_number": "Port on the adapter (always 0)"
},
@ -303,8 +283,8 @@ class VMwareHandler:
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to start a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -327,8 +307,8 @@ class VMwareHandler:
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter to stop a packet capture",
"port_number": "Port on the adapter (always 0)"
},
@ -346,12 +326,11 @@ class VMwareHandler:
yield from vm.stop_capture(adapter_number)
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/interfaces/vmnet",
parameters={
"project_id": "The UUID of the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
201: "VMnet interface allocated",
@ -366,3 +345,14 @@ class VMwareHandler:
vm.vmnets.append(vmnet)
response.set_status(201)
response.json({"vmnet": vmnet})
@Route.get(
r"/vmware/vms",
status_codes={
200: "Success",
},
description="Get all VMware VMs available")
def get_vms(request, response):
vmware_manager = VMware.instance()
vms = yield from vmware_manager.list_vms()
response.json(vms)

@ -16,12 +16,15 @@
# 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 ....compute.vpcs import VPCS
from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.compute.vpcs import VPCS
from gns3server.schemas.vpcs import (
VPCS_CREATE_SCHEMA,
VPCS_UPDATE_SCHEMA,
VPCS_OBJECT_SCHEMA
)
class VPCSHandler:
@ -30,11 +33,10 @@ class VPCSHandler:
API entry points for VPCS.
"""
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -47,20 +49,19 @@ class VPCSHandler:
def create(request, response):
vpcs = VPCS.instance()
node = yield from vpcs.create_node(request.json["name"],
request.match_info["project_id"],
request.json.get("node_id"),
console=request.json.get("console"),
startup_script=request.json.get("startup_script"))
vm = yield from vpcs.create_node(request.json["name"],
request.match_info["project_id"],
request.json.get("node_id"),
console=request.json.get("console"),
startup_script=request.json.get("startup_script"))
response.set_status(201)
response.json(node)
response.json(vm)
@classmethod
@Route.get(
r"/projects/{project_id}/vpcs/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
@ -75,12 +76,11 @@ class VPCSHandler:
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(vm)
@classmethod
@Route.put(
r"/projects/{project_id}/vpcs/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
@ -100,12 +100,11 @@ class VPCSHandler:
vm.startup_script = request.json.get("startup_script", vm.startup_script)
response.json(vm)
@classmethod
@Route.delete(
r"/projects/{project_id}/vpcs/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
@ -118,12 +117,11 @@ class VPCSHandler:
yield from VPCS.instance().delete_node(request.match_info["node_id"])
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
@ -139,12 +137,11 @@ class VPCSHandler:
yield from vm.start()
response.json(vm)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
@ -159,12 +156,11 @@ class VPCSHandler:
yield from vm.stop()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/vpcs/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Instance reloaded",
@ -182,8 +178,8 @@ class VPCSHandler:
@Route.post(
r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port where the nio should be added"
},
@ -207,12 +203,11 @@ class VPCSHandler:
response.set_status(201)
response.json(nio)
@classmethod
@Route.delete(
r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the instance",
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Network adapter where the nio is located",
"port_number": "Port from where the nio should be removed"
},

@ -18,27 +18,24 @@
import asyncio
from aiohttp.web import HTTPForbidden
from ....web.route import Route
from ....config import Config
from ....compute.project_manager import ProjectManager
from ....schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA
from ....controller import Controller
from ....controller.compute import Compute
from gns3server.web.route import Route
from gns3server.config import Config
from gns3server.compute.project_manager import ProjectManager
from gns3server.schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA
from gns3server.controller import Controller
import logging
log = logging.getLogger(__name__)
class ComputeHandler:
"""API entry points for compute management."""
"""API entry points for compute server management."""
@classmethod
@Route.post(
r"/computes",
description="Register a compute",
description="Register a compute server",
status_codes={
201: "Compute added"
201: "Compute server added"
},
input=COMPUTE_CREATE_SCHEMA,
output=COMPUTE_OBJECT_SCHEMA)
@ -48,31 +45,29 @@ class ComputeHandler:
response.set_status(201)
response.json(compute)
@classmethod
@Route.get(
r"/computes",
description="List compute nodes",
description="List of compute server",
status_codes={
200: "Compute list"
200: "Compute servers list returned"
})
def list(request, response):
controller = Controller.instance()
response.json([c for c in controller.computes.values()])
@classmethod
@Route.post(
r"/computes/shutdown",
description="Shutdown the local compute",
description="Shutdown a local compute server",
status_codes={
201: "Compute is shutting down",
403: "Compute shutdown refused"
201: "Compute server is shutting down",
403: "Compute server shutdown refused"
})
def shutdown(request, response):
config = Config.instance()
if config.get_section_config("Server").getboolean("local", False) is False:
raise HTTPForbidden(text="You can only stop a local server")
raise HTTPForbidden(text="Only a local server can be shutdown")
# close all the projects first
pm = ProjectManager.instance()
@ -97,12 +92,12 @@ class ComputeHandler:
asyncio.async(server.shutdown_server())
response.set_status(201)
@classmethod
@Route.get(
r"/computes/{compute_id:.+}",
description="Get a compute node informations",
description="Get a compute server information",
status_codes={
200: "Compute list"
200: "Compute server information returned"
},
output=COMPUTE_OBJECT_SCHEMA)
def get(request, response):

@ -17,9 +17,13 @@
import aiohttp
from ....web.route import Route
from ....schemas.link import LINK_OBJECT_SCHEMA, LINK_CAPTURE_SCHEMA
from ....controller import Controller
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.schemas.link import (
LINK_OBJECT_SCHEMA,
LINK_CAPTURE_SCHEMA
)
class LinkHandler:
@ -27,11 +31,10 @@ class LinkHandler:
API entry point for Link
"""
@classmethod
@Route.post(
r"/projects/{project_id}/links",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Link created",
@ -51,12 +54,11 @@ class LinkHandler:
response.set_status(201)
response.json(link)
@classmethod
@Route.post(
r"/projects/{project_id}/links/{link_id}/start_capture",
parameters={
"project_id": "UUID for the project",
"link_id": "UUID of the link"
"project_id": "Project UUID",
"link_id": "Link UUID"
},
status_codes={
201: "Capture started",
@ -64,7 +66,7 @@ class LinkHandler:
},
input=LINK_CAPTURE_SCHEMA,
output=LINK_OBJECT_SCHEMA,
description="Start capture on a link instance. By default we consider it as an ethernet link")
description="Start capture on a link instance. By default we consider it as an Ethernet link")
def start_capture(request, response):
controller = Controller.instance()
@ -74,12 +76,11 @@ class LinkHandler:
response.set_status(201)
response.json(link)
@classmethod
@Route.post(
r"/projects/{project_id}/links/{link_id}/stop_capture",
parameters={
"project_id": "UUID for the project",
"link_id": "UUID of the link"
"project_id": "Project UUID",
"link_id": "Link UUID"
},
status_codes={
201: "Capture stopped",
@ -95,12 +96,11 @@ class LinkHandler:
response.set_status(201)
response.json(link)
@classmethod
@Route.delete(
r"/projects/{project_id}/links/{link_id}",
parameters={
"project_id": "UUID for the project",
"link_id": "UUID of the link"
"project_id": "Project UUID",
"link_id": "Link UUID"
},
status_codes={
204: "Link deleted",
@ -116,16 +116,15 @@ class LinkHandler:
response.set_status(204)
response.json(link)
@classmethod
@Route.get(
r"/projects/{project_id}/links/{link_id}/pcap",
parameters={
"project_id": "UUID for the project",
"link_id": "UUID of the link"
"project_id": "Project UUID",
"link_id": "Link UUID"
},
description="Get the pcap from the capture",
description="Steam the pcap capture file",
status_codes={
200: "Return the file",
200: "File returned",
403: "Permission denied",
404: "The file doesn't exist"
})
@ -145,7 +144,7 @@ class LinkHandler:
response.content_type = "application/vnd.tcpdump.pcap"
response.set_status(200)
response.enable_chunked_encoding()
# Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
response.start(request)

@ -15,9 +15,13 @@
# 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 ....web.route import Route
from ....schemas.node import NODE_OBJECT_SCHEMA, NODE_UPDATE_SCHEMA
from ....controller import Controller
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.schemas.node import (
NODE_OBJECT_SCHEMA,
NODE_UPDATE_SCHEMA
)
class NodeHandler:
@ -25,11 +29,10 @@ class NodeHandler:
API entry point for node
"""
@classmethod
@Route.post(
r"/projects/{project_id}/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
@ -47,28 +50,27 @@ class NodeHandler:
response.set_status(201)
response.json(node)
@classmethod
@Route.get(
r"/projects/{project_id}/nodes",
parameters={
"project_id": "UUID for the project"
"project_id": "Project UUID"
},
status_codes={
200: "List of nodes",
200: "List of nodes returned",
},
description="List nodes of a project")
def list_nodes(request, response):
controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"])
response.json([ v for v in project.nodes.values() ])
response.json([v for v in project.nodes.values()])
@classmethod
@Route.put(
r"/projects/{project_id}/nodes/{node_id}",
status_codes={
201: "Instance created",
400: "Invalid request"
200: "Instance updated",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Update a node instance",
input=NODE_UPDATE_SCHEMA,
@ -77,25 +79,25 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
# Ignore this, because we use it only in create
# Ignore these because we only use them when creating a node
request.json.pop("node_id", None)
request.json.pop("node_type", None)
request.json.pop("compute_id", None)
yield from node.update(**request.json)
response.set_status(201)
response.set_status(200)
response.json(node)
@classmethod
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/start",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the node"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance created",
400: "Invalid request"
204: "Instance started",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Start a node instance",
output=NODE_OBJECT_SCHEMA)
@ -104,61 +106,58 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.start()
response.set_status(201)
response.json(node)
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/stop",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the node"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance created",
400: "Invalid request"
204: "Instance stopped",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Start a node instance",
description="Stop a node instance",
output=NODE_OBJECT_SCHEMA)
def stop(request, response):
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.stop()
response.set_status(201)
response.json(node)
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/suspend",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the node"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance created",
400: "Invalid request"
204: "Instance suspended",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Start a node instance",
description="Suspend a node instance",
output=NODE_OBJECT_SCHEMA)
def suspend(request, response):
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.suspend()
response.set_status(201)
response.json(node)
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/reload",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the node"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance created",
400: "Invalid request"
204: "Instance reloaded",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reload a node instance",
output=NODE_OBJECT_SCHEMA)
@ -167,23 +166,22 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.reload()
response.set_status(201)
response.json(node)
response.set_status(204)
@classmethod
@Route.delete(
r"/projects/{project_id}/nodes/{node_id}",
parameters={
"project_id": "UUID for the project",
"node_id": "UUID for the node"
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance deleted",
400: "Invalid request"
204: "Instance deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Delete a node instance")
def delete(request, response):
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.destroy()
response.set_status(201)
response.set_status(204)

@ -19,10 +19,13 @@ import aiohttp
import asyncio
from ....web.route import Route
from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA
from ....controller import Controller
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.schemas.project import (
PROJECT_OBJECT_SCHEMA,
PROJECT_CREATE_SCHEMA
)
import logging
log = logging.getLogger()
@ -30,7 +33,6 @@ log = logging.getLogger()
class ProjectHandler:
@classmethod
@Route.post(
r"/projects",
description="Create a new project on the server",
@ -43,15 +45,13 @@ class ProjectHandler:
def create_project(request, response):
controller = Controller.instance()
project = yield from controller.addProject(
name=request.json.get("name"),
path=request.json.get("path"),
project_id=request.json.get("project_id"),
temporary=request.json.get("temporary", False))
project = yield from controller.add_project(name=request.json.get("name"),
path=request.json.get("path"),
project_id=request.json.get("project_id"),
temporary=request.json.get("temporary", False))
response.set_status(201)
response.json(project)
@classmethod
@Route.get(
r"/projects",
description="List projects",
@ -60,17 +60,16 @@ class ProjectHandler:
})
def list_projects(request, response):
controller = Controller.instance()
response.json([ p for p in controller.projects.values() ])
response.json([p for p in controller.projects.values()])
@classmethod
@Route.get(
r"/projects/{project_id}",
description="Get the project",
description="Get a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "The project exist",
200: "Project information returned",
404: "The project doesn't exist"
})
def get(request, response):
@ -78,12 +77,11 @@ class ProjectHandler:
project = controller.get_project(request.match_info["project_id"])
response.json(project)
@classmethod
@Route.post(
r"/projects/{project_id}/commit",
description="Write changes on disk",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "Changes have been written on disk",
@ -96,12 +94,11 @@ class ProjectHandler:
yield from project.commit()
response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/close",
description="Close a project",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "The project has been closed",
@ -115,12 +112,11 @@ class ProjectHandler:
controller.remove_project(project)
response.set_status(204)
@classmethod
@Route.delete(
r"/projects/{project_id}",
description="Delete a project from disk",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
204: "Changes have been written on disk",
@ -134,12 +130,11 @@ class ProjectHandler:
controller.remove_project(project)
response.set_status(204)
@classmethod
@Route.get(
r"/projects/{project_id}/notifications",
description="Receive notifications about the projects",
description="Receive notifications about projects",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "End of stream",
@ -153,7 +148,7 @@ class ProjectHandler:
response.content_type = "application/json"
response.set_status(200)
response.enable_chunked_encoding()
# Very important: do not send a content lenght otherwise QT close the connection but curl can consume the Feed
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
response.content_length = None
response.start(request)
@ -165,12 +160,11 @@ class ProjectHandler:
except asyncio.futures.CancelledError as e:
break
@classmethod
@Route.get(
r"/projects/{project_id}/notifications/ws",
description="Receive notifications about the projects via Websocket",
description="Receive notifications about projects from a Websocket",
parameters={
"project_id": "The UUID of the project",
"project_id": "Project UUID",
},
status_codes={
200: "End of stream",
@ -187,8 +181,8 @@ class ProjectHandler:
with project.queue() as queue:
while True:
try:
notif = yield from queue.get_json(5)
notification = yield from queue.get_json(5)
except asyncio.futures.CancelledError as e:
break
ws.send_str(notif)
ws.send_str(notification)
return ws

@ -15,16 +15,15 @@
# 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 ....web.route import Route
from ....config import Config
from ....schemas.version import VERSION_SCHEMA
from ....version import __version__
from gns3server.web.route import Route
from gns3server.config import Config
from gns3server.schemas.version import VERSION_SCHEMA
from gns3server.version import __version__
from aiohttp.web import HTTPConflict
class VersionHandler:
@classmethod
@Route.get(
r"/version",
description="Retrieve the server version number",
@ -35,7 +34,6 @@ class VersionHandler:
local_server = config.get_section_config("Server").getboolean("local", False)
response.json({"version": __version__, "local": local_server})
@classmethod
@Route.post(
r"/version",
description="Check if version is the same as the server",

@ -1,5 +1,5 @@
#
# Copyright (C) 2015 GNS3 Technologies Inc.
# Copyright (C) 2016 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
@ -15,58 +15,51 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ..web.route import Route
from ..controller import Controller
from ..compute.port_manager import PortManager
from ..compute.project_manager import ProjectManager
from ..version import __version__
from gns3server.web.route import Route
from gns3server.controller import Controller
from gns3server.compute.port_manager import PortManager
from gns3server.compute.project_manager import ProjectManager
from gns3server.version import __version__
class IndexHandler:
@classmethod
@Route.get(
r"/",
description="Home page for GNS3Server"
description="Home page of the GNS3 server"
)
def index(request, response):
response.template("index.html")
@classmethod
@Route.get(
r"/compute",
description="Ressources used by GNS3 Compute"
description="Resources used by the GNS3 compute servers"
)
def compute(request, response):
response.template("compute.html",
port_manager=PortManager.instance(),
project_manager=ProjectManager.instance()
)
project_manager=ProjectManager.instance())
@classmethod
@Route.get(
r"/controller",
description="Ressources used by GNS3 Controller"
description="Resources used by the GNS3 controller server"
)
def controller(request, response):
response.template("controller.html",
controller=Controller.instance()
)
controller=Controller.instance())
@classmethod
@Route.get(
r"/projects/{project_id}",
description="Ressources used by GNS3 Controller"
description="List of the GNS3 projects"
)
def project(request, response):
controller = Controller.instance()
response.template("project.html",
project=controller.get_project(request.match_info["project_id"]))
@classmethod
@Route.get(
r"/v1/version",
description="Old API"
description="Old 1.0 API"
)
def get_v1(request, response):
response.json({"version": __version__})

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
# Copyright (C) 2016 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
@ -22,17 +22,16 @@ import io
import tarfile
import asyncio
from ..config import Config
from ..web.route import Route
from ..utils.images import remove_checksum, md5sum
from gns3server.config import Config
from gns3server.web.route import Route
from gns3server.utils.images import remove_checksum, md5sum
class UploadHandler:
@classmethod
@Route.get(
r"/upload",
description="Manage upload of GNS3 images",
description="List binary images",
api_version=None
)
def index(request, response):
@ -50,10 +49,9 @@ class UploadHandler:
uploaded_files.append(iourc_path)
response.template("upload.html", files=uploaded_files)
@classmethod
@Route.post(
r"/upload",
description="Manage upload of GNS3 images",
description="Upload binary images",
api_version=None
)
def upload(request, response):
@ -95,16 +93,14 @@ class UploadHandler:
return
response.redirect("/upload")
@classmethod
@Route.get(
r"/backup/images.tar",
description="Backup GNS3 images",
description="Backup binary images",
api_version=None
)
def backup_images(request, response):
yield from UploadHandler._backup_directory(request, response, UploadHandler.image_directory())
@classmethod
@Route.get(
r"/backup/projects.tar",
description="Backup GNS3 projects",

@ -18,7 +18,7 @@
NODE_LIST_IMAGES_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "List of disk images",
"description": "List of binary images",
"type": "array",
"items": [
{

@ -71,7 +71,7 @@ def test_vboxmanage_path(manager, tmpdir):
assert manager.find_vboxmanage() == path
def test_list_images(manager, loop):
def test_list_vms(manager, loop):
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Carriage',
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
@ -92,7 +92,7 @@ def test_list_images(manager, loop):
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute") as mock:
mock.side_effect = execute_mock
vms = loop.run_until_complete(asyncio.async(manager.list_images()))
vms = loop.run_until_complete(asyncio.async(manager.list_vms()))
assert vms == [
{"vmname": "Windows 8.1", "ram": 512},
{"vmname": "Linux Microcore 4.7.1", "ram": 256}

@ -121,18 +121,18 @@ def test_addProject(controller, async_run):
uuid1 = str(uuid.uuid4())
uuid2 = str(uuid.uuid4())
async_run(controller.addProject(project_id=uuid1))
async_run(controller.add_project(project_id=uuid1))
assert len(controller.projects) == 1
async_run(controller.addProject(project_id=uuid1))
async_run(controller.add_project(project_id=uuid1))
assert len(controller.projects) == 1
async_run(controller.addProject(project_id=uuid2))
async_run(controller.add_project(project_id=uuid2))
assert len(controller.projects) == 2
def test_remove_project(controller, async_run):
uuid1 = str(uuid.uuid4())
project1 = async_run(controller.addProject(project_id=uuid1))
project1 = async_run(controller.add_project(project_id=uuid1))
assert len(controller.projects) == 1
controller.remove_project(project1)
@ -146,13 +146,13 @@ def test_addProject_with_compute(controller, async_run):
compute.post = MagicMock()
controller._computes = {"test1": compute}
project1 = async_run(controller.addProject(project_id=uuid1))
project1 = async_run(controller.add_project(project_id=uuid1))
def test_getProject(controller, async_run):
uuid1 = str(uuid.uuid4())
project = async_run(controller.addProject(project_id=uuid1))
project = async_run(controller.add_project(project_id=uuid1))
assert controller.get_project(uuid1) == project
with pytest.raises(aiohttp.web.HTTPNotFound):
assert controller.get_project("dsdssd")

@ -96,9 +96,16 @@ def test_docker_delete(http_compute, vm):
assert response.status == 204
def test_docker_reload(http_compute, vm):
def test_docker_pause(http_compute, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/pause".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called
assert response.status == 204
def test_docker_unpause(http_compute, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/unpause".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called
assert response.status == 204

@ -151,17 +151,17 @@ def fake_file(tmpdir):
return path
def test_vms(http_compute, tmpdir, fake_dynamips, fake_file):
def test_images(http_compute, tmpdir, fake_dynamips, fake_file):
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir), example=True):
response = http_compute.get("/dynamips/nodes")
response = http_compute.get("/dynamips/images")
assert response.status == 200
assert response.json == [{"filename": "7200.bin", "path": "7200.bin"}]
def test_upload_vm(http_compute, tmpdir):
def test_upload_image(http_compute, tmpdir):
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/dynamips/nodes/test2", body="TEST", raw=True)
response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True)
assert response.status == 204
with open(str(tmpdir / "test2")) as f:
@ -172,11 +172,11 @@ def test_upload_vm(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_vm_permission_denied(http_compute, tmpdir):
def test_upload_image_permission_denied(http_compute, tmpdir):
with open(str(tmpdir / "test2.tmp"), "w+") as f:
f.write("")
os.chmod(str(tmpdir / "test2.tmp"), 0)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/dynamips/nodes/test2", body="TEST", raw=True)
response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True)
assert response.status == 409

@ -328,17 +328,17 @@ def test_get_configs_with_startup_config_file(http_compute, project, vm):
assert response.json["startup_config_content"] == "TEST"
def test_vms(http_compute, tmpdir, fake_iou_bin):
def test_images(http_compute, tmpdir, fake_iou_bin):
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)):
response = http_compute.get("/iou/nodes", example=True)
response = http_compute.get("/iou/images", example=True)
assert response.status == 200
assert response.json == [{"filename": "iou.bin", "path": "iou.bin"}]
def test_upload_vm(http_compute, tmpdir):
def test_image_vm(http_compute, tmpdir):
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/iou/nodes/test2", body="TEST", raw=True)
response = http_compute.post("/iou/images/test2", body="TEST", raw=True)
assert response.status == 204
with open(str(tmpdir / "test2")) as f:

@ -224,16 +224,16 @@ def test_qemu_list_binaries_filter(http_compute, vm):
assert response.json == ret
def test_vms(http_compute, tmpdir, fake_qemu_vm):
def test_images(http_compute, tmpdir, fake_qemu_vm):
response = http_compute.get("/qemu/nodes")
response = http_compute.get("/qemu/images")
assert response.status == 200
assert response.json == [{"filename": "linux载.img", "path": "linux载.img"}]
def test_upload_vm(http_compute, tmpdir):
def test_upload_image(http_compute, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/nodes/test2", body="TEST", raw=True)
response = http_compute.post("/qemu/images/test2", body="TEST", raw=True)
assert response.status == 204
with open(str(tmpdir / "test2")) as f:
@ -244,9 +244,9 @@ def test_upload_vm(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_vm_ova(http_compute, tmpdir):
def test_upload_image_ova(http_compute, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/nodes/test2.ova/test2.vmdk", body="TEST", raw=True)
response = http_compute.post("/qemu/images/test2.ova/test2.vmdk", body="TEST", raw=True)
assert response.status == 204
with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f:
@ -257,19 +257,19 @@ def test_upload_vm_ova(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_vm_forbiden_location(http_compute, tmpdir):
def test_upload_image_forbiden_location(http_compute, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/nodes/../../test2", body="TEST", raw=True)
response = http_compute.post("/qemu/images/../../test2", body="TEST", raw=True)
assert response.status == 403
def test_upload_vm_permission_denied(http_compute, tmpdir):
def test_upload_image_permission_denied(http_compute, tmpdir):
with open(str(tmpdir / "test2.tmp"), "w+") as f:
f.write("")
os.chmod(str(tmpdir / "test2.tmp"), 0)
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/nodes/test2", body="TEST", raw=True)
response = http_compute.post("/qemu/images/test2", body="TEST", raw=True)
assert response.status == 409

@ -45,7 +45,7 @@ def compute(http_controller, async_run):
@pytest.fixture
def project(http_controller, async_run):
return async_run(Controller.instance().addProject())
return async_run(Controller.instance().add_project())
def test_create_link(http_controller, tmpdir, project, compute, async_run):

@ -44,7 +44,7 @@ def compute(http_controller, async_run):
@pytest.fixture
def project(http_controller, async_run):
return async_run(Controller.instance().addProject())
return async_run(Controller.instance().add_project())
@pytest.fixture
@ -103,7 +103,7 @@ def test_update_node(http_controller, tmpdir, project, compute, node):
"startup_script": "echo test"
}
}, example=True)
assert response.status == 201
assert response.status == 200
assert response.json["name"] == "test"
assert "name" not in response.json["properties"]
@ -113,17 +113,14 @@ def test_start_node(http_controller, tmpdir, project, compute, node):
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/start".format(project.id, node.id), example=True)
assert response.status == 201
assert response.json["name"] == node.name
assert response.status == 204
def test_stop_node(http_controller, tmpdir, project, compute, node):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/stop".format(project.id, node.id), example=True)
assert response.status == 201
assert response.json["name"] == node.name
assert response.status == 204
def test_suspend_node(http_controller, tmpdir, project, compute, node):
@ -131,8 +128,7 @@ def test_suspend_node(http_controller, tmpdir, project, compute, node):
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/suspend".format(project.id, node.id), example=True)
assert response.status == 201
assert response.json["name"] == node.name
assert response.status == 204
def test_reload_node(http_controller, tmpdir, project, compute, node):
@ -140,8 +136,7 @@ def test_reload_node(http_controller, tmpdir, project, compute, node):
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/reload".format(project.id, node.id), example=True)
assert response.status == 201
assert response.json["name"] == node.name
assert response.status == 204
def test_delete_node(http_controller, tmpdir, project, compute, node):
@ -149,4 +144,4 @@ def test_delete_node(http_controller, tmpdir, project, compute, node):
compute.post = AsyncioMagicMock()
response = http_controller.delete("/projects/{}/nodes/{}".format(project.id, node.id), example=True)
assert response.status == 201
assert response.status == 204

@ -33,7 +33,7 @@ def test_index(http_root):
def test_controller(http_root, async_run):
project = async_run(Controller.instance().addProject(name="test"))
project = async_run(Controller.instance().add_project(name="test"))
response = http_root.get('/controller')
assert "test" in response.html
assert response.status == 200
@ -45,7 +45,7 @@ def test_compute(http_root):
def test_project(http_root, async_run):
project = async_run(Controller.instance().addProject(name="test"))
project = async_run(Controller.instance().add_project(name="test"))
response = http_root.get('/projects/{}'.format(project.id))
assert response.status == 200

Loading…
Cancel
Save