1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 09:18:08 +00:00

Handlers: cleanup and fixes.

This commit is contained in:
grossmj 2016-05-13 18:00:07 -06:00
parent 885d93be02
commit f81d35cc29
33 changed files with 723 additions and 766 deletions

View File

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

View File

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

View File

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

View File

@ -17,16 +17,15 @@
from aiohttp.web import HTTPForbidden from aiohttp.web import HTTPForbidden
from ....web.route import Route from gns3server.web.route import Route
from ....config import Config from gns3server.config import Config
class ConfigHandler: class ConfigHandler:
@classmethod
@Route.post( @Route.post(
r"/config/reload", r"/config/reload",
description="Check if version is the same as the server", description="Reload the server configuration file",
status_codes={ status_codes={
201: "Config reload", 201: "Config reload",
403: "Config reload refused" 403: "Config reload refused"
@ -35,6 +34,6 @@ class ConfigHandler:
config = Config.instance() config = Config.instance()
if config.get_section_config("Server").getboolean("local", False) is False: 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() config.reload()
response.set_status(201) response.set_status(201)

View File

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

View File

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

View File

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

View File

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

View File

@ -15,19 +15,18 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from ....web.route import Route from gns3server.web.route import Route
from ....compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
from ....compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from ....utils.interfaces import interfaces from gns3server.utils.interfaces import interfaces
class NetworkHandler: class NetworkHandler:
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/ports/udp", r"/projects/{project_id}/ports/udp",
parameters={ parameters={
"project_id": "The UUID of the project", "project_id": "Project UUID",
}, },
status_codes={ status_codes={
201: "UDP port allocated", 201: "UDP port allocated",
@ -43,7 +42,6 @@ class NetworkHandler:
response.set_status(201) response.set_status(201)
response.json({"udp_port": udp_port}) response.json({"udp_port": udp_port})
@classmethod
@Route.get( @Route.get(
r"/interfaces", r"/interfaces",
description="List all the network interfaces available on the server") description="List all the network interfaces available on the server")

View File

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

View File

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

View File

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

View File

@ -15,16 +15,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from ....web.route import Route from gns3server.web.route import Route
from ....config import Config from gns3server.config import Config
from ....schemas.version import VERSION_SCHEMA from gns3server.schemas.version import VERSION_SCHEMA
from ....version import __version__ from gns3server.version import __version__
from aiohttp.web import HTTPConflict from aiohttp.web import HTTPConflict
class VersionHandler: class VersionHandler:
@classmethod
@Route.get( @Route.get(
r"/version", r"/version",
description="Retrieve the server version number", description="Retrieve the server version number",
@ -35,7 +34,6 @@ class VersionHandler:
local_server = config.get_section_config("Server").getboolean("local", False) local_server = config.get_section_config("Server").getboolean("local", False)
response.json({"version": __version__, "local": local_server}) response.json({"version": __version__, "local": local_server})
@classmethod
@Route.post( @Route.post(
r"/version", r"/version",
description="Check if version is the same as the server", description="Check if version is the same as the server",

View File

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

View File

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

View File

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

View File

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

View File

@ -17,9 +17,13 @@
import aiohttp import aiohttp
from ....web.route import Route from gns3server.web.route import Route
from ....schemas.link import LINK_OBJECT_SCHEMA, LINK_CAPTURE_SCHEMA from gns3server.controller import Controller
from ....controller import Controller
from gns3server.schemas.link import (
LINK_OBJECT_SCHEMA,
LINK_CAPTURE_SCHEMA
)
class LinkHandler: class LinkHandler:
@ -27,11 +31,10 @@ class LinkHandler:
API entry point for Link API entry point for Link
""" """
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/links", r"/projects/{project_id}/links",
parameters={ parameters={
"project_id": "UUID for the project" "project_id": "Project UUID"
}, },
status_codes={ status_codes={
201: "Link created", 201: "Link created",
@ -51,12 +54,11 @@ class LinkHandler:
response.set_status(201) response.set_status(201)
response.json(link) response.json(link)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/links/{link_id}/start_capture", r"/projects/{project_id}/links/{link_id}/start_capture",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"link_id": "UUID of the link" "link_id": "Link UUID"
}, },
status_codes={ status_codes={
201: "Capture started", 201: "Capture started",
@ -64,7 +66,7 @@ class LinkHandler:
}, },
input=LINK_CAPTURE_SCHEMA, input=LINK_CAPTURE_SCHEMA,
output=LINK_OBJECT_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): def start_capture(request, response):
controller = Controller.instance() controller = Controller.instance()
@ -74,12 +76,11 @@ class LinkHandler:
response.set_status(201) response.set_status(201)
response.json(link) response.json(link)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/links/{link_id}/stop_capture", r"/projects/{project_id}/links/{link_id}/stop_capture",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"link_id": "UUID of the link" "link_id": "Link UUID"
}, },
status_codes={ status_codes={
201: "Capture stopped", 201: "Capture stopped",
@ -95,12 +96,11 @@ class LinkHandler:
response.set_status(201) response.set_status(201)
response.json(link) response.json(link)
@classmethod
@Route.delete( @Route.delete(
r"/projects/{project_id}/links/{link_id}", r"/projects/{project_id}/links/{link_id}",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"link_id": "UUID of the link" "link_id": "Link UUID"
}, },
status_codes={ status_codes={
204: "Link deleted", 204: "Link deleted",
@ -116,16 +116,15 @@ class LinkHandler:
response.set_status(204) response.set_status(204)
response.json(link) response.json(link)
@classmethod
@Route.get( @Route.get(
r"/projects/{project_id}/links/{link_id}/pcap", r"/projects/{project_id}/links/{link_id}/pcap",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"link_id": "UUID of the link" "link_id": "Link UUID"
}, },
description="Get the pcap from the capture", description="Steam the pcap capture file",
status_codes={ status_codes={
200: "Return the file", 200: "File returned",
403: "Permission denied", 403: "Permission denied",
404: "The file doesn't exist" 404: "The file doesn't exist"
}) })
@ -145,7 +144,7 @@ class LinkHandler:
response.content_type = "application/vnd.tcpdump.pcap" response.content_type = "application/vnd.tcpdump.pcap"
response.set_status(200) response.set_status(200)
response.enable_chunked_encoding() 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.content_length = None
response.start(request) response.start(request)

View File

@ -15,9 +15,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from ....web.route import Route from gns3server.web.route import Route
from ....schemas.node import NODE_OBJECT_SCHEMA, NODE_UPDATE_SCHEMA from gns3server.controller import Controller
from ....controller import Controller
from gns3server.schemas.node import (
NODE_OBJECT_SCHEMA,
NODE_UPDATE_SCHEMA
)
class NodeHandler: class NodeHandler:
@ -25,11 +29,10 @@ class NodeHandler:
API entry point for node API entry point for node
""" """
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/nodes", r"/projects/{project_id}/nodes",
parameters={ parameters={
"project_id": "UUID for the project" "project_id": "Project UUID"
}, },
status_codes={ status_codes={
201: "Instance created", 201: "Instance created",
@ -47,28 +50,27 @@ class NodeHandler:
response.set_status(201) response.set_status(201)
response.json(node) response.json(node)
@classmethod
@Route.get( @Route.get(
r"/projects/{project_id}/nodes", r"/projects/{project_id}/nodes",
parameters={ parameters={
"project_id": "UUID for the project" "project_id": "Project UUID"
}, },
status_codes={ status_codes={
200: "List of nodes", 200: "List of nodes returned",
}, },
description="List nodes of a project") description="List nodes of a project")
def list_nodes(request, response): def list_nodes(request, response):
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"]) 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( @Route.put(
r"/projects/{project_id}/nodes/{node_id}", r"/projects/{project_id}/nodes/{node_id}",
status_codes={ status_codes={
201: "Instance created", 200: "Instance updated",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Update a node instance", description="Update a node instance",
input=NODE_UPDATE_SCHEMA, input=NODE_UPDATE_SCHEMA,
@ -77,25 +79,25 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_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_id", None)
request.json.pop("node_type", None) request.json.pop("node_type", None)
request.json.pop("compute_id", None) request.json.pop("compute_id", None)
yield from node.update(**request.json) yield from node.update(**request.json)
response.set_status(201) response.set_status(200)
response.json(node) response.json(node)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/nodes/{node_id}/start", r"/projects/{project_id}/nodes/{node_id}/start",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"node_id": "UUID for the node" "node_id": "Node UUID"
}, },
status_codes={ status_codes={
201: "Instance created", 204: "Instance started",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Start a node instance", description="Start a node instance",
output=NODE_OBJECT_SCHEMA) output=NODE_OBJECT_SCHEMA)
@ -104,61 +106,58 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"]) node = project.get_node(request.match_info["node_id"])
yield from node.start() yield from node.start()
response.set_status(201) response.set_status(204)
response.json(node)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/nodes/{node_id}/stop", r"/projects/{project_id}/nodes/{node_id}/stop",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"node_id": "UUID for the node" "node_id": "Node UUID"
}, },
status_codes={ status_codes={
201: "Instance created", 204: "Instance stopped",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Start a node instance", description="Stop a node instance",
output=NODE_OBJECT_SCHEMA) output=NODE_OBJECT_SCHEMA)
def stop(request, response): def stop(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"]) node = project.get_node(request.match_info["node_id"])
yield from node.stop() yield from node.stop()
response.set_status(201) response.set_status(204)
response.json(node)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/nodes/{node_id}/suspend", r"/projects/{project_id}/nodes/{node_id}/suspend",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"node_id": "UUID for the node" "node_id": "Node UUID"
}, },
status_codes={ status_codes={
201: "Instance created", 204: "Instance suspended",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Start a node instance", description="Suspend a node instance",
output=NODE_OBJECT_SCHEMA) output=NODE_OBJECT_SCHEMA)
def suspend(request, response): def suspend(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"]) node = project.get_node(request.match_info["node_id"])
yield from node.suspend() yield from node.suspend()
response.set_status(201) response.set_status(204)
response.json(node)
@classmethod
@Route.post( @Route.post(
r"/projects/{project_id}/nodes/{node_id}/reload", r"/projects/{project_id}/nodes/{node_id}/reload",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"node_id": "UUID for the node" "node_id": "Node UUID"
}, },
status_codes={ status_codes={
201: "Instance created", 204: "Instance reloaded",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Reload a node instance", description="Reload a node instance",
output=NODE_OBJECT_SCHEMA) output=NODE_OBJECT_SCHEMA)
@ -167,23 +166,22 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"]) node = project.get_node(request.match_info["node_id"])
yield from node.reload() yield from node.reload()
response.set_status(201) response.set_status(204)
response.json(node)
@classmethod
@Route.delete( @Route.delete(
r"/projects/{project_id}/nodes/{node_id}", r"/projects/{project_id}/nodes/{node_id}",
parameters={ parameters={
"project_id": "UUID for the project", "project_id": "Project UUID",
"node_id": "UUID for the node" "node_id": "Node UUID"
}, },
status_codes={ status_codes={
201: "Instance deleted", 204: "Instance deleted",
400: "Invalid request" 400: "Invalid request",
404: "Instance doesn't exist"
}, },
description="Delete a node instance") description="Delete a node instance")
def delete(request, response): def delete(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"]) node = project.get_node(request.match_info["node_id"])
yield from node.destroy() yield from node.destroy()
response.set_status(201) response.set_status(204)

View File

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

View File

@ -15,16 +15,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from ....web.route import Route from gns3server.web.route import Route
from ....config import Config from gns3server.config import Config
from ....schemas.version import VERSION_SCHEMA from gns3server.schemas.version import VERSION_SCHEMA
from ....version import __version__ from gns3server.version import __version__
from aiohttp.web import HTTPConflict from aiohttp.web import HTTPConflict
class VersionHandler: class VersionHandler:
@classmethod
@Route.get( @Route.get(
r"/version", r"/version",
description="Retrieve the server version number", description="Retrieve the server version number",
@ -35,7 +34,6 @@ class VersionHandler:
local_server = config.get_section_config("Server").getboolean("local", False) local_server = config.get_section_config("Server").getboolean("local", False)
response.json({"version": __version__, "local": local_server}) response.json({"version": __version__, "local": local_server})
@classmethod
@Route.post( @Route.post(
r"/version", r"/version",
description="Check if version is the same as the server", description="Check if version is the same as the server",

View File

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

View File

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

View File

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

View File

@ -71,7 +71,7 @@ def test_vboxmanage_path(manager, tmpdir):
assert manager.find_vboxmanage() == path 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}', vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Carriage', '"Carriage',
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', '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: with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute") as mock:
mock.side_effect = execute_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 == [ assert vms == [
{"vmname": "Windows 8.1", "ram": 512}, {"vmname": "Windows 8.1", "ram": 512},
{"vmname": "Linux Microcore 4.7.1", "ram": 256} {"vmname": "Linux Microcore 4.7.1", "ram": 256}

View File

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

View File

@ -96,9 +96,16 @@ def test_docker_delete(http_compute, vm):
assert response.status == 204 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: 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 mock.called
assert response.status == 204 assert response.status == 204

View File

@ -151,17 +151,17 @@ def fake_file(tmpdir):
return path 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): 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.status == 200
assert response.json == [{"filename": "7200.bin", "path": "7200.bin"}] 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),): 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 assert response.status == 204
with open(str(tmpdir / "test2")) as f: with open(str(tmpdir / "test2")) as f:
@ -172,11 +172,11 @@ def test_upload_vm(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" 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: with open(str(tmpdir / "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(str(tmpdir / "test2.tmp"), 0) os.chmod(str(tmpdir / "test2.tmp"), 0)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(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 == 409 assert response.status == 409

View File

@ -328,17 +328,17 @@ def test_get_configs_with_startup_config_file(http_compute, project, vm):
assert response.json["startup_config_content"] == "TEST" 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)): 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.status == 200
assert response.json == [{"filename": "iou.bin", "path": "iou.bin"}] 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),): 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 assert response.status == 204
with open(str(tmpdir / "test2")) as f: with open(str(tmpdir / "test2")) as f:

View File

@ -224,16 +224,16 @@ def test_qemu_list_binaries_filter(http_compute, vm):
assert response.json == ret 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.status == 200
assert response.json == [{"filename": "linux载.img", "path": "linux载.img"}] 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),): 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 assert response.status == 204
with open(str(tmpdir / "test2")) as f: with open(str(tmpdir / "test2")) as f:
@ -244,9 +244,9 @@ def test_upload_vm(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" 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),): 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 assert response.status == 204
with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: 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" 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),): 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 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: with open(str(tmpdir / "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(str(tmpdir / "test2.tmp"), 0) os.chmod(str(tmpdir / "test2.tmp"), 0)
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(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 == 409 assert response.status == 409

View File

@ -45,7 +45,7 @@ def compute(http_controller, async_run):
@pytest.fixture @pytest.fixture
def project(http_controller, async_run): 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): def test_create_link(http_controller, tmpdir, project, compute, async_run):

View File

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

View File

@ -33,7 +33,7 @@ def test_index(http_root):
def test_controller(http_root, async_run): 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') response = http_root.get('/controller')
assert "test" in response.html assert "test" in response.html
assert response.status == 200 assert response.status == 200
@ -45,7 +45,7 @@ def test_compute(http_root):
def test_project(http_root, async_run): 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)) response = http_root.get('/projects/{}'.format(project.id))
assert response.status == 200 assert response.status == 200