diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 57b2c1e1..2c499b98 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -22,7 +22,6 @@ Docker server module. import asyncio import logging import aiohttp -import urllib import json from gns3server.utils import parse_version diff --git a/gns3server/compute/virtualbox/__init__.py b/gns3server/compute/virtualbox/__init__.py index 92136b32..5b006d1b 100644 --- a/gns3server/compute/virtualbox/__init__.py +++ b/gns3server/compute/virtualbox/__init__.py @@ -155,7 +155,7 @@ class VirtualBox(BaseManager): continue @asyncio.coroutine - def list_images(self): + def list_vms(self): """ Gets VirtualBox VM list. """ diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 531db407..9dafcc0f 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -31,7 +31,7 @@ log = logging.getLogger(__name__) class Controller: - """The controller manage multiple gns3 computes""" + """The controller manage multiple gns3 compute servers""" def __init__(self): self._computes = {} @@ -97,7 +97,7 @@ class Controller: # We disallow to create from the outside the if compute_id == 'local': - return self._createLocalCompute() + return self._create_local_compute() if compute_id not in self._computes: compute = Compute(compute_id=compute_id, controller=self, **kwargs) @@ -105,7 +105,7 @@ class Controller: self.save() return self._computes[compute_id] - def _createLocalCompute(self): + def _create_local_compute(self): """ Create the local compute node. It's the controller itself """ @@ -139,7 +139,7 @@ class Controller: raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id)) @asyncio.coroutine - def addProject(self, project_id=None, **kwargs): + def add_project(self, project_id=None, **kwargs): """ Create a project or return an existing project diff --git a/gns3server/handlers/api/compute/config_handler.py b/gns3server/handlers/api/compute/config_handler.py index aa3b1a99..8da644cb 100644 --- a/gns3server/handlers/api/compute/config_handler.py +++ b/gns3server/handlers/api/compute/config_handler.py @@ -17,16 +17,15 @@ from aiohttp.web import HTTPForbidden -from ....web.route import Route -from ....config import Config +from gns3server.web.route import Route +from gns3server.config import Config class ConfigHandler: - @classmethod @Route.post( r"/config/reload", - description="Check if version is the same as the server", + description="Reload the server configuration file", status_codes={ 201: "Config reload", 403: "Config reload refused" @@ -35,6 +34,6 @@ class ConfigHandler: config = Config.instance() if config.get_section_config("Server").getboolean("local", False) is False: - raise HTTPForbidden(text="You can only reload the configuration for a local server") + raise HTTPForbidden(text="The configuration can only be reloaded on a local server") config.reload() response.set_status(201) diff --git a/gns3server/handlers/api/compute/docker_handler.py b/gns3server/handlers/api/compute/docker_handler.py index ae43b9fb..740c8a6b 100644 --- a/gns3server/handlers/api/compute/docker_handler.py +++ b/gns3server/handlers/api/compute/docker_handler.py @@ -18,40 +18,26 @@ import os from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....compute.docker import Docker +from gns3server.web.route import Route +from gns3server.compute.docker import Docker +from gns3server.schemas.node import NODE_CAPTURE_SCHEMA +from gns3server.schemas.nio import NIO_SCHEMA -from ....schemas.docker import ( +from gns3server.schemas.docker import ( DOCKER_CREATE_SCHEMA, DOCKER_OBJECT_SCHEMA, DOCKER_UPDATE_SCHEMA, DOCKER_LIST_IMAGES_SCHEMA ) -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....schemas.nio import NIO_SCHEMA class DockerHandler: - """API entry points for Docker.""" + """API entry points for Docker containers.""" - @classmethod - @Route.get( - r"/docker/images", - status_codes={ - 200: "Success", - }, - output=DOCKER_LIST_IMAGES_SCHEMA, - description="Get all available Docker images") - def show(request, response): - docker_manager = Docker.instance() - images = yield from docker_manager.list_images() - response.json(images) - - @classmethod @Route.post( r"/projects/{project_id}/docker/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -63,33 +49,32 @@ class DockerHandler: output=DOCKER_OBJECT_SCHEMA) def create(request, response): docker_manager = Docker.instance() - vm = yield from docker_manager.create_node(request.json.pop("name"), - request.match_info["project_id"], - request.json.get("node_id"), - image=request.json.pop("image"), - start_command=request.json.get("start_command"), - environment=request.json.get("environment"), - adapters=request.json.get("adapters"), - console=request.json.get("console"), - console_type=request.json.get("console_type"), - console_resolution=request.json.get("console_resolution", "1024x768"), - console_http_port=request.json.get("console_http_port", 80), - console_http_path=request.json.get("console_http_path", "/"), - aux=request.json.get("aux")) + container = yield from docker_manager.create_node(request.json.pop("name"), + request.match_info["project_id"], + request.json.get("node_id"), + image=request.json.pop("image"), + start_command=request.json.get("start_command"), + environment=request.json.get("environment"), + adapters=request.json.get("adapters"), + console=request.json.get("console"), + console_type=request.json.get("console_type"), + console_resolution=request.json.get("console_resolution", "1024x768"), + console_http_port=request.json.get("console_http_port", 80), + console_http_path=request.json.get("console_http_path", "/"), + aux=request.json.get("aux")) for name, value in request.json.items(): - if name != "_node_id": - if hasattr(vm, name) and getattr(vm, name) != value: - setattr(vm, name, value) + if name != "node_id": + if hasattr(container, name) and getattr(container, name) != value: + setattr(container, name, value) response.set_status(201) - response.json(vm) + response.json(container) - @classmethod @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/start", parameters={ - "project_id": "UUID of the project", - "node_id": "ID of the container" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance started", @@ -101,16 +86,15 @@ class DockerHandler: output=DOCKER_OBJECT_SCHEMA) def start(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.start() + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.start() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/stop", parameters={ - "project_id": "UUID of the project", - "node_id": "ID of the container" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -122,16 +106,15 @@ class DockerHandler: output=DOCKER_OBJECT_SCHEMA) def stop(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.stop() + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/reload", parameters={ - "project_id": "UUID of the project", - "node_id": "ID of the container" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance restarted", @@ -143,16 +126,15 @@ class DockerHandler: output=DOCKER_OBJECT_SCHEMA) def reload(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.restart() + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.restart() response.set_status(204) - @classmethod @Route.delete( r"/projects/{project_id}/docker/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "ID for the container", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance deleted", @@ -162,16 +144,15 @@ class DockerHandler: description="Delete a Docker container") def delete(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.delete() + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.delete() response.set_status(204) - @classmethod @Route.post( - r"/projects/{project_id}/docker/nodes/{node_id}/suspend", + r"/projects/{project_id}/docker/nodes/{node_id}/pause", parameters={ - "project_id": "UUID of the project", - "node_id": "ID of the container" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance paused", @@ -181,17 +162,37 @@ class DockerHandler: description="Pause a Docker container", input=DOCKER_CREATE_SCHEMA, output=DOCKER_OBJECT_SCHEMA) - def suspend(request, response): + def pause(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.pause() + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.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) @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "ID of the container", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter where the nio should be added", "port_number": "Port on the adapter" }, @@ -205,21 +206,20 @@ class DockerHandler: output=NIO_SCHEMA) def create_nio(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) nio_type = request.json["type"] - if nio_type not in ("nio_udp"): + if nio_type != "nio_udp": raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type)) nio = docker_manager.create_nio(int(request.match_info["adapter_number"]), request.json) - yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio) + yield from container.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio) response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "ID of the container", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter where the nio should be added", "port_number": "Port on the adapter" }, @@ -231,16 +231,15 @@ class DockerHandler: description="Remove a NIO from a Docker container") def delete_nio(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"])) + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + yield from container.adapter_remove_nio_binding(int(request.match_info["adapter_number"])) response.set_status(204) - @classmethod @Route.put( r"/projects/{project_id}/docker/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -254,25 +253,25 @@ class DockerHandler: def update(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - vm.name = request.json.get("name", vm.name) - vm.console = request.json.get("console", vm.console) - vm.aux = request.json.get("aux", vm.aux) - vm.console_type = request.json.get("console_type", vm.console_type) - vm.console_resolution = request.json.get("console_resolution", vm.console_resolution) - vm.console_http_port = request.json.get("console_http_port", vm.console_http_port) - vm.console_http_path = request.json.get("console_http_path", vm.console_http_path) - vm.start_command = request.json.get("start_command", vm.start_command) - vm.environment = request.json.get("environment", vm.environment) - vm.adapters = request.json.get("adapters", vm.adapters) - yield from vm.update() - response.json(vm) + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + container.name = request.json.get("name", container.name) + container.console = request.json.get("console", container.console) + container.aux = request.json.get("aux", container.aux) + container.console_type = request.json.get("console_type", container.console_type) + container.console_resolution = request.json.get("console_resolution", container.console_resolution) + container.console_http_port = request.json.get("console_http_port", container.console_http_port) + container.console_http_path = request.json.get("console_http_path", container.console_http_path) + container.start_command = request.json.get("start_command", container.start_command) + container.environment = request.json.get("environment", container.environment) + container.adapters = request.json.get("adapters", container.adapters) + yield from container.update() + response.json(container) @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to start a packet capture", "port_number": "Port on the adapter" }, @@ -282,25 +281,25 @@ class DockerHandler: 404: "Instance doesn't exist", 409: "Node not started" }, - description="Start a packet capture on a Docker VM instance", + description="Start a packet capture on a Docker container instance", input=NODE_CAPTURE_SCHEMA) def start_capture(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) adapter_number = int(request.match_info["adapter_number"]) - pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"]) + pcap_file_path = os.path.join(container.project.capture_working_directory(), request.json["capture_file_name"]) - if not vm.is_running(): - raise HTTPConflict(text="Cannot capture traffic on a non started VM") - yield from vm.start_capture(adapter_number, pcap_file_path) + if not container.is_running(): + raise HTTPConflict(text="Cannot capture traffic on a non started Docker container") + yield from container.start_capture(adapter_number, pcap_file_path) response.json({"pcap_file_path": str(pcap_file_path)}) @Route.post( r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to stop a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -308,17 +307,29 @@ class DockerHandler: 204: "Capture stopped", 400: "Invalid request", 404: "Instance doesn't exist", - 409: "VM not started" + 409: "Container not started" }, - description="Stop a packet capture on a IOU VM instance") + description="Stop a packet capture on a Docker container instance") def stop_capture(request, response): docker_manager = Docker.instance() - vm = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - if not vm.is_running(): - raise HTTPConflict(text="Cannot capture traffic on a non started VM") + if not container.is_running(): + raise HTTPConflict(text="Cannot capture traffic on a non started Docker container") adapter_number = int(request.match_info["adapter_number"]) - yield from vm.stop_capture(adapter_number) + yield from container.stop_capture(adapter_number) response.set_status(204) + + @Route.get( + r"/docker/images", + status_codes={ + 200: "Success", + }, + output=DOCKER_LIST_IMAGES_SCHEMA, + description="Get all available Docker images") + def show(request, response): + docker_manager = Docker.instance() + images = yield from docker_manager.list_images() + response.json(images) diff --git a/gns3server/handlers/api/compute/dynamips_device_handler.py b/gns3server/handlers/api/compute/dynamips_device_handler.py index 1cd738eb..4ff4b791 100644 --- a/gns3server/handlers/api/compute/dynamips_device_handler.py +++ b/gns3server/handlers/api/compute/dynamips_device_handler.py @@ -17,13 +17,17 @@ import os import asyncio -from ....web.route import Route -from ....schemas.dynamips_device import DEVICE_CREATE_SCHEMA -from ....schemas.dynamips_device import DEVICE_UPDATE_SCHEMA -from ....schemas.dynamips_device import DEVICE_OBJECT_SCHEMA -from ....schemas.dynamips_device import DEVICE_NIO_SCHEMA -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....compute.dynamips import Dynamips + +from gns3server.web.route import Route +from gns3server.schemas.node import NODE_CAPTURE_SCHEMA +from gns3server.compute.dynamips import Dynamips + +from gns3server.schemas.dynamips_device import ( + DEVICE_CREATE_SCHEMA, + DEVICE_UPDATE_SCHEMA, + DEVICE_OBJECT_SCHEMA, + DEVICE_NIO_SCHEMA +) class DynamipsDeviceHandler: @@ -32,11 +36,10 @@ class DynamipsDeviceHandler: API entry points for Dynamips devices. """ - @classmethod @Route.post( r"/projects/{project_id}/dynamips/devices", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -57,12 +60,11 @@ class DynamipsDeviceHandler: response.set_status(201) response.json(device) - @classmethod @Route.get( r"/projects/{project_id}/dynamips/devices/{device_id}", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance" + "project_id": "Project UUID", + "device_id": "Device UUID" }, status_codes={ 200: "Success", @@ -77,12 +79,11 @@ class DynamipsDeviceHandler: device = dynamips_manager.get_device(request.match_info["device_id"], project_id=request.match_info["project_id"]) response.json(device) - @classmethod @Route.put( r"/projects/{project_id}/dynamips/devices/{device_id}", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance" + "project_id": "Project UUID", + "device_id": "Device UUID" }, status_codes={ 200: "Instance updated", @@ -107,12 +108,11 @@ class DynamipsDeviceHandler: response.json(device) - @classmethod @Route.delete( r"/projects/{project_id}/dynamips/devices/{device_id}", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance" + "project_id": "Project UUID", + "device_id": "Device UUID" }, status_codes={ 204: "Instance deleted", @@ -129,8 +129,8 @@ class DynamipsDeviceHandler: @Route.post( r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance", + "project_id": "Project UUID", + "device_id": "Device UUID", "port_number": "Port on the device" }, status_codes={ @@ -162,12 +162,11 @@ class DynamipsDeviceHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance", + "project_id": "Project UUID", + "device_id": "Device UUID", "port_number": "Port on the device" }, status_codes={ @@ -188,8 +187,8 @@ class DynamipsDeviceHandler: @Route.post( r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance", + "project_id": "Project UUID", + "device_id": "Device UUID", "port_number": "Port on the device" }, status_codes={ @@ -211,8 +210,8 @@ class DynamipsDeviceHandler: @Route.post( r"/projects/{project_id}/dynamips/devices/{device_id}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "device_id": "UUID for the instance", + "project_id": "Project UUID", + "device_id": "Device UUID", "port_number": "Port on the device" }, status_codes={ diff --git a/gns3server/handlers/api/compute/dynamips_vm_handler.py b/gns3server/handlers/api/compute/dynamips_vm_handler.py index 6748ccb8..05ec3e5f 100644 --- a/gns3server/handlers/api/compute/dynamips_vm_handler.py +++ b/gns3server/handlers/api/compute/dynamips_vm_handler.py @@ -19,17 +19,23 @@ import os import sys import base64 -from ....web.route import Route -from ....schemas.nio import NIO_SCHEMA -from ....schemas.dynamips_vm import VM_CREATE_SCHEMA -from ....schemas.dynamips_vm import VM_UPDATE_SCHEMA -from ....schemas.dynamips_vm import VM_OBJECT_SCHEMA -from ....schemas.dynamips_vm import VM_CONFIGS_SCHEMA -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....schemas.node import NODE_LIST_IMAGES_SCHEMA -from ....compute.dynamips import Dynamips -from ....compute.dynamips.dynamips_error import DynamipsError -from ....compute.project_manager import ProjectManager +from gns3server.web.route import Route +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.compute.dynamips import Dynamips +from gns3server.compute.dynamips.dynamips_error import DynamipsError +from gns3server.compute.project_manager import ProjectManager + +from gns3server.schemas.node import ( + NODE_CAPTURE_SCHEMA, + NODE_LIST_IMAGES_SCHEMA, +) + +from gns3server.schemas.dynamips_vm import ( + VM_CREATE_SCHEMA, + VM_UPDATE_SCHEMA, + VM_OBJECT_SCHEMA, + VM_CONFIGS_SCHEMA +) DEFAULT_CHASSIS = { "c1700": "1720", @@ -44,11 +50,10 @@ class DynamipsVMHandler: API entry points for Dynamips VMs. """ - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -66,24 +71,23 @@ class DynamipsVMHandler: if platform in DEFAULT_CHASSIS: default_chassis = DEFAULT_CHASSIS[platform] vm = yield from dynamips_manager.create_node(request.json.pop("name"), - request.match_info["project_id"], - request.json.get("node_id"), - request.json.get("dynamips_id"), - platform, - console=request.json.get("console"), - aux=request.json.get("aux"), - chassis=request.json.pop("chassis", default_chassis)) + request.match_info["project_id"], + request.json.get("node_id"), + request.json.get("dynamips_id"), + platform, + console=request.json.get("console"), + aux=request.json.get("aux"), + chassis=request.json.pop("chassis", default_chassis)) yield from dynamips_manager.update_vm_settings(vm, request.json) response.set_status(201) response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/dynamips/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", @@ -98,12 +102,11 @@ class DynamipsVMHandler: vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/dynamips/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -121,12 +124,11 @@ class DynamipsVMHandler: yield from dynamips_manager.update_vm_settings(vm, request.json) response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/dynamips/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", @@ -138,16 +140,14 @@ class DynamipsVMHandler: # check the project_id exists ProjectManager.instance().get_project(request.match_info["project_id"]) - yield from Dynamips.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance started", @@ -166,12 +166,11 @@ class DynamipsVMHandler: yield from vm.start() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -186,12 +185,11 @@ class DynamipsVMHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/suspend", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance suspended", @@ -206,12 +204,11 @@ class DynamipsVMHandler: yield from vm.suspend() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/resume", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance resumed", @@ -226,12 +223,11 @@ class DynamipsVMHandler: yield from vm.resume() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance reloaded", @@ -249,8 +245,8 @@ class DynamipsVMHandler: @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter where the nio should be added", "port_number": "Port on the adapter" }, @@ -273,12 +269,11 @@ class DynamipsVMHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter from where the nio should be removed", "port_number": "Port on the adapter" }, @@ -301,8 +296,8 @@ class DynamipsVMHandler: @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to start a packet capture", "port_number": "Port on the adapter" }, @@ -334,8 +329,8 @@ class DynamipsVMHandler: @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to stop a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -356,6 +351,10 @@ class DynamipsVMHandler: @Route.get( r"/projects/{project_id}/dynamips/nodes/{node_id}/configs", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + }, status_codes={ 200: "Configs retrieved", 400: "Invalid request", @@ -375,7 +374,7 @@ class DynamipsVMHandler: startup_config_content = base64.b64decode(startup_config_base64).decode("utf-8", errors='replace') result["startup_config_content"] = startup_config_content else: - # nvram doesn't contain anything if the router has not been started at least once + # The NVRAM doesn't contain anything if the router has not been started at least once # in this case just use the startup-config file if vm.startup_config: startup_config_path = os.path.join(module_workdir, vm.startup_config) @@ -392,7 +391,7 @@ class DynamipsVMHandler: private_config_content = base64.b64decode(private_config_base64).decode("utf-8", errors='replace') result["private_config_content"] = private_config_content else: - # nvram doesn't contain anything if the router has not been started at least once + # The NVRAM doesn't contain anything if the router has not been started at least once # in this case just use the private-config file if vm.private_config: private_config_path = os.path.join(module_workdir, vm.private_config) @@ -410,6 +409,10 @@ class DynamipsVMHandler: @Route.post( r"/projects/{project_id}/dynamips/nodes/{node_id}/configs/save", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + }, status_codes={ 200: "Configs saved", 400: "Invalid request", @@ -425,6 +428,10 @@ class DynamipsVMHandler: @Route.get( r"/projects/{project_id}/dynamips/nodes/{node_id}/idlepc_proposals", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + }, status_codes={ 200: "Idle-PCs retrieved", 400: "Invalid request", @@ -442,6 +449,10 @@ class DynamipsVMHandler: @Route.get( r"/projects/{project_id}/dynamips/nodes/{node_id}/auto_idlepc", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + }, status_codes={ 200: "Best Idle-pc value found", 400: "Invalid request", @@ -457,28 +468,31 @@ class DynamipsVMHandler: response.json({"idlepc": idlepc}) @Route.get( - r"/dynamips/nodes", + r"/dynamips/images", status_codes={ - 200: "List of Dynamips VM retrieved", + 200: "List of Dynamips IOS images", }, - description="Retrieve the list of Dynamips VMS", + description="Retrieve the list of Dynamips IOS images", output=NODE_LIST_IMAGES_SCHEMA) - def list_vms(request, response): + def list_images(request, response): dynamips_manager = Dynamips.instance() - vms = yield from dynamips_manager.list_images() + images = yield from dynamips_manager.list_images() response.set_status(200) - response.json(vms) + response.json(images) @Route.post( - r"/dynamips/nodes/{path}", + r"/dynamips/images/{filename:.+}", + parameters={ + "filename": "Image filename" + }, status_codes={ - 204: "Image uploaded", + 204: "Upload a Dynamips IOS image", }, raw=True, - description="Upload Dynamips image") + description="Upload a Dynamips IOS image") def upload_image(request, response): dynamips_manager = Dynamips.instance() - yield from dynamips_manager.write_image(request.match_info["path"], request.content) + yield from dynamips_manager.write_image(request.match_info["filename"], request.content) response.set_status(204) diff --git a/gns3server/handlers/api/compute/iou_handler.py b/gns3server/handlers/api/compute/iou_handler.py index 0362f9bd..82018201 100644 --- a/gns3server/handlers/api/compute/iou_handler.py +++ b/gns3server/handlers/api/compute/iou_handler.py @@ -18,16 +18,22 @@ import os from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....schemas.nio import NIO_SCHEMA -from ....schemas.iou import IOU_CREATE_SCHEMA -from ....schemas.iou import IOU_START_SCHEMA -from ....schemas.iou import IOU_UPDATE_SCHEMA -from ....schemas.iou import IOU_OBJECT_SCHEMA -from ....schemas.iou import IOU_CONFIGS_SCHEMA -from ....schemas.node import NODE_LIST_IMAGES_SCHEMA -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....compute.iou import IOU +from gns3server.web.route import Route +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.compute.iou import IOU + +from gns3server.schemas.node import ( + NODE_CAPTURE_SCHEMA, + NODE_LIST_IMAGES_SCHEMA, +) + +from gns3server.schemas.iou import ( + IOU_CREATE_SCHEMA, + IOU_START_SCHEMA, + IOU_UPDATE_SCHEMA, + IOU_OBJECT_SCHEMA, + IOU_CONFIGS_SCHEMA, +) class IOUHandler: @@ -36,11 +42,10 @@ class IOUHandler: API entry points for IOU. """ - @classmethod @Route.post( r"/projects/{project_id}/iou/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -72,19 +77,18 @@ class IOUHandler: response.set_status(201) response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/iou/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", 400: "Invalid request", 404: "Instance doesn't exist" }, - description="Get a IOU instance", + description="Get an IOU instance", output=IOU_OBJECT_SCHEMA) def show(request, response): @@ -92,12 +96,11 @@ class IOUHandler: vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/iou/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -105,7 +108,7 @@ class IOUHandler: 404: "Instance doesn't exist", 409: "Conflict" }, - description="Update a IOU instance", + description="Update an IOU instance", input=IOU_UPDATE_SCHEMA, output=IOU_OBJECT_SCHEMA) def update(request, response): @@ -122,30 +125,28 @@ class IOUHandler: vm.private_config = request.json.get("private_config_content") response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/iou/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", 400: "Invalid request", 404: "Instance doesn't exist" }, - description="Delete a IOU instance") + description="Delete an IOU instance") def delete(request, response): yield from IOU.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance started", @@ -154,7 +155,7 @@ class IOUHandler: }, input=IOU_START_SCHEMA, output=IOU_OBJECT_SCHEMA, - description="Start a IOU instance") + description="Start an IOU instance") def start(request, response): iou_manager = IOU.instance() @@ -167,19 +168,18 @@ class IOUHandler: yield from vm.start() response.json(vm) - @classmethod @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", 400: "Invalid request", 404: "Instance doesn't exist" }, - description="Stop a IOU instance") + description="Stop an IOU instance") def stop(request, response): iou_manager = IOU.instance() @@ -187,19 +187,18 @@ class IOUHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance reloaded", 400: "Invalid request", 404: "Instance doesn't exist" }, - description="Reload a IOU instance") + description="Reload an IOU instance") def reload(request, response): iou_manager = IOU.instance() @@ -210,8 +209,8 @@ class IOUHandler: @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port where the nio should be added" }, @@ -235,12 +234,11 @@ class IOUHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port from where the nio should be removed" }, @@ -260,8 +258,8 @@ class IOUHandler: @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to start a packet capture", "port_number": "Port on the adapter" }, @@ -271,7 +269,7 @@ class IOUHandler: 404: "Instance doesn't exist", 409: "VM not started" }, - description="Start a packet capture on a IOU VM instance", + description="Start a packet capture on an IOU VM instance", input=NODE_CAPTURE_SCHEMA) def start_capture(request, response): @@ -288,8 +286,8 @@ class IOUHandler: @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to stop a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -299,7 +297,7 @@ class IOUHandler: 404: "Instance doesn't exist", 409: "VM not started" }, - description="Stop a packet capture on a IOU VM instance") + description="Stop a packet capture on an IOU VM instance") def stop_capture(request, response): iou_manager = IOU.instance() @@ -315,6 +313,10 @@ class IOUHandler: @Route.get( r"/projects/{project_id}/iou/nodes/{node_id}/configs", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID" + }, status_codes={ 200: "Configs retrieved", 400: "Invalid request", @@ -352,6 +354,10 @@ class IOUHandler: @Route.post( r"/projects/{project_id}/iou/nodes/{node_id}/configs/save", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID" + }, status_codes={ 200: "Configs saved", 400: "Invalid request", @@ -366,28 +372,31 @@ class IOUHandler: response.set_status(200) @Route.get( - r"/iou/nodes", + r"/iou/images", status_codes={ - 200: "List of IOU VM retrieved", + 200: "List of IOU images", }, - description="Retrieve the list of IOU VMS", + description="Retrieve the list of IOU images", output=NODE_LIST_IMAGES_SCHEMA) - def list_iou_nodes(request, response): + def list_iou_images(request, response): iou_manager = IOU.instance() - iou_nodes = yield from iou_manager.list_images() + images = yield from iou_manager.list_images() response.set_status(200) - response.json(iou_nodes) + response.json(images) @Route.post( - r"/iou/nodes/{path}", + r"/iou/images/{filename:.+}", + parameters={ + "filename": "Image filename" + }, status_codes={ 204: "Image uploaded", }, raw=True, - description="Upload IOU image.") + description="Upload an IOU image") def upload_image(request, response): iou_manager = IOU.instance() - yield from iou_manager.write_image(request.match_info["path"], request.content) + yield from iou_manager.write_image(request.match_info["filename"], request.content) response.set_status(204) diff --git a/gns3server/handlers/api/compute/network_handler.py b/gns3server/handlers/api/compute/network_handler.py index e2da87ad..375c600c 100644 --- a/gns3server/handlers/api/compute/network_handler.py +++ b/gns3server/handlers/api/compute/network_handler.py @@ -15,19 +15,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ....web.route import Route -from ....compute.port_manager import PortManager -from ....compute.project_manager import ProjectManager -from ....utils.interfaces import interfaces +from gns3server.web.route import Route +from gns3server.compute.port_manager import PortManager +from gns3server.compute.project_manager import ProjectManager +from gns3server.utils.interfaces import interfaces class NetworkHandler: - @classmethod @Route.post( r"/projects/{project_id}/ports/udp", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 201: "UDP port allocated", @@ -43,7 +42,6 @@ class NetworkHandler: response.set_status(201) response.json({"udp_port": udp_port}) - @classmethod @Route.get( r"/interfaces", description="List all the network interfaces available on the server") diff --git a/gns3server/handlers/api/compute/notification_handler.py b/gns3server/handlers/api/compute/notification_handler.py index e0903f29..19fa64d9 100644 --- a/gns3server/handlers/api/compute/notification_handler.py +++ b/gns3server/handlers/api/compute/notification_handler.py @@ -17,17 +17,16 @@ import asyncio -from ....web.route import Route -from ....compute.notification_manager import NotificationManager from aiohttp.web import WebSocketResponse +from gns3server.web.route import Route +from gns3server.compute.notification_manager import NotificationManager class NotificationHandler: - @classmethod @Route.get( r"/notifications/ws", - description="Send notifications about what happend using websockets") + description="Send notifications using Websockets") def notifications(request, response): notifications = NotificationManager.instance() ws = WebSocketResponse() @@ -36,8 +35,8 @@ class NotificationHandler: with notifications.queue() as queue: while True: try: - notif = yield from queue.get_json(5) - except asyncio.futures.CancelledError as e: + notification = yield from queue.get_json(5) + except asyncio.futures.CancelledError: break - ws.send_str(notif) + ws.send_str(notification) return ws diff --git a/gns3server/handlers/api/compute/project_handler.py b/gns3server/handlers/api/compute/project_handler.py index 9315c0e5..2690c161 100644 --- a/gns3server/handlers/api/compute/project_handler.py +++ b/gns3server/handlers/api/compute/project_handler.py @@ -22,10 +22,17 @@ import os import psutil import tempfile -from ....web.route import Route -from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA, PROJECT_UPDATE_SCHEMA, PROJECT_FILE_LIST_SCHEMA, PROJECT_LIST_SCHEMA -from ....compute.project_manager import ProjectManager -from ....compute import MODULES +from gns3server.web.route import Route +from gns3server.compute.project_manager import ProjectManager +from gns3server.compute import MODULES + +from gns3server.schemas.project import ( + PROJECT_OBJECT_SCHEMA, + PROJECT_CREATE_SCHEMA, + PROJECT_UPDATE_SCHEMA, + PROJECT_FILE_LIST_SCHEMA, + PROJECT_LIST_SCHEMA +) import logging log = logging.getLogger() @@ -33,13 +40,12 @@ log = logging.getLogger() class ProjectHandler: - # How many clients has subcribe to notifications + # How many clients have subscribed to notifications _notifications_listening = {} - @classmethod @Route.get( r"/projects", - description="List projects opened on the server", + description="List all projects opened on the server", status_codes={ 200: "Project list", }, @@ -51,13 +57,12 @@ class ProjectHandler: response.set_status(200) response.json(list(pm.projects)) - @classmethod @Route.post( r"/projects", description="Create a new project on the server", status_codes={ 201: "Project created", - 403: "You are not allowed to modify this property", + 403: "Forbidden to create a project", 409: "Project already created" }, output=PROJECT_OBJECT_SCHEMA, @@ -74,12 +79,11 @@ class ProjectHandler: response.set_status(201) response.json(p) - @classmethod @Route.get( r"/projects/{project_id}", description="Get project information", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 200: "Success", @@ -92,16 +96,15 @@ class ProjectHandler: project = pm.get_project(request.match_info["project_id"]) response.json(project) - @classmethod @Route.put( r"/projects/{project_id}", description="Update a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 200: "The project has been updated", - 403: "You are not allowed to modify this property", + 200: "Project updated", + 403: "Forbidden to update this project", 404: "The project doesn't exist" }, output=PROJECT_OBJECT_SCHEMA, @@ -118,16 +121,15 @@ class ProjectHandler: for module in MODULES: yield from module.instance().project_moved(project) yield from project.clean_old_path(old_path) - # Very important we need to remove temporary flag after moving the project + # Very important: we need to remove temporary flag after moving the project project.temporary = request.json.get("temporary", project.temporary) response.json(project) - @classmethod @Route.post( r"/projects/{project_id}/commit", description="Write changes on disk", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 204: "Changes have been written on disk", @@ -140,15 +142,14 @@ class ProjectHandler: yield from project.commit() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/close", description="Close a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 204: "The project has been closed", + 204: "Project closed", 404: "The project doesn't exist" }) def close(request, response): @@ -160,15 +161,14 @@ class ProjectHandler: pm.remove_project(project.id) del ProjectHandler._notifications_listening[project.id] else: - log.warning("Skip project closing, another client is listening for project informations") + log.warning("Skip project closing, another client is listening for project notifications") response.set_status(204) - @classmethod @Route.delete( r"/projects/{project_id}", description="Delete a project from disk", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 204: "Changes have been written on disk", @@ -182,12 +182,11 @@ class ProjectHandler: pm.remove_project(project.id) response.set_status(204) - @classmethod @Route.get( r"/projects/{project_id}/notifications", - description="Receive notifications about the projects", + description="Receive notifications about the project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 200: "End of stream", @@ -201,7 +200,7 @@ class ProjectHandler: response.content_type = "application/json" response.set_status(200) response.enable_chunked_encoding() - # Very important: do not send a content lenght otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None response.start(request) @@ -226,30 +225,27 @@ class ProjectHandler: if project.id in ProjectHandler._notifications_listening: ProjectHandler._notifications_listening[project.id] -= 1 - @classmethod def _getPingMessage(cls): """ - The ping message is regulary send to the client to - keep the connection open. We send with it some informations - about server load. + Ping messages are regularly sent to the client to + keep the connection open. We send with it some information about server load. :returns: hash """ stats = {} - # Non blocking call in order to get cpu usage. First call will return 0 + # Non blocking call in order to get cpu usage. First call will return 0. stats["cpu_usage_percent"] = psutil.cpu_percent(interval=None) stats["memory_usage_percent"] = psutil.virtual_memory().percent return {"action": "ping", "event": stats} - @classmethod @Route.get( r"/projects/{project_id}/files", description="List files of a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 200: "Return list of files", + 200: "Return a list of files", 404: "The project doesn't exist" }, output=PROJECT_FILE_LIST_SCHEMA) @@ -261,15 +257,14 @@ class ProjectHandler: response.json(files) response.set_status(200) - @classmethod @Route.get( r"/projects/{project_id}/files/{path:.+}", - description="Get a file of a project", + description="Get a file from a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 200: "Return the file", + 200: "File returned", 403: "Permission denied", 404: "The file doesn't exist" }) @@ -280,7 +275,7 @@ class ProjectHandler: path = request.match_info["path"] path = os.path.normpath(path) - # Raise error if user try to escape + # Raise an error if user try to escape if path[0] == ".": raise aiohttp.web.HTTPForbidden path = os.path.join(project.path, path) @@ -288,7 +283,7 @@ class ProjectHandler: response.content_type = "application/octet-stream" response.set_status(200) response.enable_chunked_encoding() - # Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None try: @@ -305,15 +300,14 @@ class ProjectHandler: except PermissionError: raise aiohttp.web.HTTPForbidden() - @classmethod @Route.get( r"/projects/{project_id}/stream/{path:.+}", description="Stream a file from a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 200: "Return the file", + 200: "File returned", 403: "Permission denied", 404: "The file doesn't exist" }) @@ -324,7 +318,7 @@ class ProjectHandler: path = request.match_info["path"] path = os.path.normpath(path) - # Raise error if user try to escape + # Raise an error if user try to escape if path[0] == ".": raise aiohttp.web.HTTPForbidden path = os.path.join(project.path, path) @@ -332,7 +326,7 @@ class ProjectHandler: response.content_type = "application/octet-stream" response.set_status(200) response.enable_chunked_encoding() - # Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None try: @@ -349,16 +343,15 @@ class ProjectHandler: except PermissionError: raise aiohttp.web.HTTPForbidden() - @classmethod @Route.post( r"/projects/{project_id}/files/{path:.+}", - description="Get a file of a project", + description="Write a file to a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, raw=True, status_codes={ - 200: "Return the file", + 200: "File returned", 403: "Permission denied", 404: "The path doesn't exist" }) @@ -389,16 +382,15 @@ class ProjectHandler: except PermissionError: raise aiohttp.web.HTTPForbidden() - @classmethod @Route.get( r"/projects/{project_id}/export", description="Export a project as a portable archive", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, raw=True, status_codes={ - 200: "Return the file", + 200: "File returned", 404: "The project doesn't exist" }) def export_project(request, response): @@ -408,7 +400,7 @@ class ProjectHandler: response.content_type = 'application/gns3project' response.headers['CONTENT-DISPOSITION'] = 'attachment; filename="{}.gns3project"'.format(project.name) response.enable_chunked_encoding() - # Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None response.start(request) @@ -418,18 +410,17 @@ class ProjectHandler: yield from response.write_eof() - @classmethod @Route.post( r"/projects/{project_id}/import", description="Import a project from a portable archive", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, raw=True, output=PROJECT_OBJECT_SCHEMA, status_codes={ 200: "Project imported", - 403: "You are not allowed to modify this property" + 403: "Forbidden to import project" }) def import_project(request, response): @@ -437,11 +428,9 @@ class ProjectHandler: project_id = request.match_info["project_id"] project = pm.create_project(project_id=project_id) - # We write the content to a temporary location - # and after extract all. It could be more optimal to stream - # this but it's not implemented in Python. - #  - # Spooled mean the file is temporary keep in ram until max_size + # We write the content to a temporary location and after we extract it all. + # It could be more optimal to stream this but it is not implemented in Python. + # Spooled means the file is temporary kept in memory until max_size is reached try: with tempfile.SpooledTemporaryFile(max_size=10000) as temp: while True: diff --git a/gns3server/handlers/api/compute/qemu_handler.py b/gns3server/handlers/api/compute/qemu_handler.py index 23e42dca..fa7f2c13 100644 --- a/gns3server/handlers/api/compute/qemu_handler.py +++ b/gns3server/handlers/api/compute/qemu_handler.py @@ -19,19 +19,23 @@ import sys import os.path from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....compute.project_manager import ProjectManager -from ....schemas.nio import NIO_SCHEMA -from ....schemas.qemu import QEMU_CREATE_SCHEMA -from ....schemas.qemu import QEMU_UPDATE_SCHEMA -from ....schemas.qemu import QEMU_OBJECT_SCHEMA -from ....schemas.qemu import QEMU_BINARY_FILTER_SCHEMA -from ....schemas.qemu import QEMU_BINARY_LIST_SCHEMA -from ....schemas.qemu import QEMU_CAPABILITY_LIST_SCHEMA -from ....schemas.qemu import QEMU_IMAGE_CREATE_SCHEMA -from ....schemas.node import NODE_LIST_IMAGES_SCHEMA -from ....compute.qemu import Qemu -from ....config import Config + +from gns3server.web.route import Route +from gns3server.compute.project_manager import ProjectManager +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.schemas.node import NODE_LIST_IMAGES_SCHEMA +from gns3server.compute.qemu import Qemu +from gns3server.config import Config + +from gns3server.schemas.qemu import ( + QEMU_CREATE_SCHEMA, + QEMU_UPDATE_SCHEMA, + QEMU_OBJECT_SCHEMA, + QEMU_BINARY_LIST_SCHEMA, + QEMU_BINARY_FILTER_SCHEMA, + QEMU_CAPABILITY_LIST_SCHEMA, + QEMU_IMAGE_CREATE_SCHEMA +) class QEMUHandler: @@ -40,11 +44,10 @@ class QEMUHandler: API entry points for QEMU. """ - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -73,12 +76,11 @@ class QEMUHandler: response.set_status(201) response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/qemu/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", @@ -93,12 +95,11 @@ class QEMUHandler: vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/qemu/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -120,12 +121,11 @@ class QEMUHandler: response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/qemu/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", @@ -138,12 +138,11 @@ class QEMUHandler: yield from Qemu.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance started", @@ -156,20 +155,18 @@ class QEMUHandler: qemu_manager = Qemu.instance() vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - if sys.platform.startswith("linux") and qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) \ - and "-no-kvm" not in vm.options: + if sys.platform.startswith("linux") and qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) and "-no-kvm" not in vm.options: pm = ProjectManager.instance() if pm.check_hardware_virtualization(vm) is False: raise HTTPConflict(text="Cannot start VM with KVM enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox") yield from vm.start() response.json(vm) - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "vm_node": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -184,12 +181,11 @@ class QEMUHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance reloaded", @@ -204,12 +200,11 @@ class QEMUHandler: yield from vm.reload() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/suspend", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance suspended", @@ -224,12 +219,11 @@ class QEMUHandler: yield from vm.suspend() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/resume", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance resumed", @@ -247,8 +241,8 @@ class QEMUHandler: @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port on the adapter (always 0)" }, @@ -272,12 +266,11 @@ class QEMUHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port on the adapter (always 0)" }, @@ -294,7 +287,6 @@ class QEMUHandler: yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"])) response.set_status(204) - @classmethod @Route.get( r"/qemu/binaries", status_codes={ @@ -310,7 +302,6 @@ class QEMUHandler: binaries = yield from Qemu.binary_list(request.json.get("archs", None)) response.json(binaries) - @classmethod @Route.get( r"/qemu/img-binaries", status_codes={ @@ -340,7 +331,6 @@ class QEMUHandler: capabilities["kvm"] = kvms response.json(capabilities) - @classmethod @Route.post( r"/qemu/img", status_codes={ @@ -363,28 +353,31 @@ class QEMUHandler: response.set_status(201) @Route.get( - r"/qemu/nodes", + r"/qemu/images", status_codes={ - 200: "List of Qemu images retrieved", + 200: "List of Qemu images", }, description="Retrieve the list of Qemu images", output=NODE_LIST_IMAGES_SCHEMA) - def list_vm_nodes(request, response): + def list_qemu_images(request, response): qemu_manager = Qemu.instance() - vm_nodes = yield from qemu_manager.list_images() + images = yield from qemu_manager.list_images() response.set_status(200) - response.json(vm_nodes) + response.json(images) @Route.post( - r"/qemu/nodes/{path:.+}", + r"/qemu/images/{filename:.+}", + parameters={ + "filename": "Image filename" + }, status_codes={ 204: "Image uploaded", }, raw=True, - description="Upload Qemu image.") + description="Upload Qemu image") def upload_image(request, response): qemu_manager = Qemu.instance() - yield from qemu_manager.write_image(request.match_info["path"], request.content) + yield from qemu_manager.write_image(request.match_info["filename"], request.content) response.set_status(204) diff --git a/gns3server/handlers/api/compute/version_handler.py b/gns3server/handlers/api/compute/version_handler.py index dad6d31b..b45f4aa9 100644 --- a/gns3server/handlers/api/compute/version_handler.py +++ b/gns3server/handlers/api/compute/version_handler.py @@ -15,16 +15,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ....web.route import Route -from ....config import Config -from ....schemas.version import VERSION_SCHEMA -from ....version import __version__ +from gns3server.web.route import Route +from gns3server.config import Config +from gns3server.schemas.version import VERSION_SCHEMA +from gns3server.version import __version__ from aiohttp.web import HTTPConflict class VersionHandler: - @classmethod @Route.get( r"/version", description="Retrieve the server version number", @@ -35,7 +34,6 @@ class VersionHandler: local_server = config.get_section_config("Server").getboolean("local", False) response.json({"version": __version__, "local": local_server}) - @classmethod @Route.post( r"/version", description="Check if version is the same as the server", diff --git a/gns3server/handlers/api/compute/virtualbox_handler.py b/gns3server/handlers/api/compute/virtualbox_handler.py index d23a24cd..c0f28d4f 100644 --- a/gns3server/handlers/api/compute/virtualbox_handler.py +++ b/gns3server/handlers/api/compute/virtualbox_handler.py @@ -18,15 +18,17 @@ import os from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....schemas.nio import NIO_SCHEMA -from ....schemas.virtualbox import VBOX_CREATE_SCHEMA -from ....schemas.virtualbox import VBOX_UPDATE_SCHEMA -from ....schemas.virtualbox import VBOX_OBJECT_SCHEMA -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....compute.virtualbox import VirtualBox -from ....compute.project_manager import ProjectManager +from gns3server.web.route import Route +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.schemas.node import NODE_CAPTURE_SCHEMA +from gns3server.compute.virtualbox import VirtualBox +from gns3server.compute.project_manager import ProjectManager +from gns3server.schemas.virtualbox import ( + VBOX_CREATE_SCHEMA, + VBOX_UPDATE_SCHEMA, + VBOX_OBJECT_SCHEMA +) class VirtualBoxHandler: @@ -34,24 +36,10 @@ class VirtualBoxHandler: API entry points for VirtualBox. """ - @classmethod - @Route.get( - r"/virtualbox/vms", - status_codes={ - 200: "Success", - }, - description="Get all available VirtualBox VMs") - def index(request, response): - - vbox_manager = VirtualBox.instance() - vms = yield from vbox_manager.list_images() - response.json(vms) - - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -88,12 +76,11 @@ class VirtualBoxHandler: response.set_status(201) response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/virtualbox/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", @@ -108,12 +95,11 @@ class VirtualBoxHandler: vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/virtualbox/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -153,12 +139,11 @@ class VirtualBoxHandler: response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/virtualbox/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", @@ -170,16 +155,14 @@ class VirtualBoxHandler: # check the project_id exists ProjectManager.instance().get_project(request.match_info["project_id"]) - yield from VirtualBox.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance started", @@ -198,12 +181,11 @@ class VirtualBoxHandler: yield from vm.start() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -218,12 +200,11 @@ class VirtualBoxHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/suspend", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance suspended", @@ -238,12 +219,11 @@ class VirtualBoxHandler: yield from vm.suspend() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/resume", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance resumed", @@ -258,12 +238,11 @@ class VirtualBoxHandler: yield from vm.resume() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance reloaded", @@ -281,8 +260,8 @@ class VirtualBoxHandler: @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter where the nio should be added", "port_number": "Port on the adapter (always 0)" }, @@ -306,12 +285,11 @@ class VirtualBoxHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter from where the nio should be removed", "port_number": "Port on the adapter (always 0)" }, @@ -331,8 +309,8 @@ class VirtualBoxHandler: @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to start a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -355,8 +333,8 @@ class VirtualBoxHandler: @Route.post( r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to stop a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -372,3 +350,14 @@ class VirtualBoxHandler: vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm.stop_capture(int(request.match_info["adapter_number"])) response.set_status(204) + + @Route.get( + r"/virtualbox/vms", + status_codes={ + 200: "Success", + }, + description="Get all available VirtualBox VMs") + def get_vms(request, response): + vbox_manager = VirtualBox.instance() + vms = yield from vbox_manager.list_vms() + response.json(vms) diff --git a/gns3server/handlers/api/compute/vmware_handler.py b/gns3server/handlers/api/compute/vmware_handler.py index 1dfe2408..da268c12 100644 --- a/gns3server/handlers/api/compute/vmware_handler.py +++ b/gns3server/handlers/api/compute/vmware_handler.py @@ -18,14 +18,17 @@ import os from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....schemas.vmware import VMWARE_CREATE_SCHEMA -from ....schemas.vmware import VMWARE_UPDATE_SCHEMA -from ....schemas.vmware import VMWARE_OBJECT_SCHEMA -from ....schemas.node import NODE_CAPTURE_SCHEMA -from ....schemas.nio import NIO_SCHEMA -from ....compute.vmware import VMware -from ....compute.project_manager import ProjectManager +from gns3server.web.route import Route +from gns3server.schemas.node import NODE_CAPTURE_SCHEMA +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.compute.vmware import VMware +from gns3server.compute.project_manager import ProjectManager + +from gns3server.schemas.vmware import ( + VMWARE_CREATE_SCHEMA, + VMWARE_UPDATE_SCHEMA, + VMWARE_OBJECT_SCHEMA +) class VMwareHandler: @@ -34,24 +37,10 @@ class VMwareHandler: API entry points for VMware. """ - @classmethod - @Route.get( - r"/vmware/vms", - status_codes={ - 200: "Success", - }, - description="Get all VMware VMs available") - def index(request, response): - - vmware_manager = VMware.instance() - vms = yield from vmware_manager.list_vms() - response.json(vms) - - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -79,12 +68,11 @@ class VMwareHandler: response.set_status(201) response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/vmware/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", @@ -99,12 +87,11 @@ class VMwareHandler: vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/vmware/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -126,12 +113,11 @@ class VMwareHandler: response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/vmware/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", @@ -146,12 +132,11 @@ class VMwareHandler: yield from VMware.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance started", @@ -170,12 +155,11 @@ class VMwareHandler: yield from vm.start() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -190,12 +174,11 @@ class VMwareHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/suspend", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance suspended", @@ -210,12 +193,11 @@ class VMwareHandler: yield from vm.suspend() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/resume", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance resumed", @@ -230,12 +212,11 @@ class VMwareHandler: yield from vm.resume() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance reloaded", @@ -253,8 +234,8 @@ class VMwareHandler: @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter where the nio should be added", "port_number": "Port on the adapter (always 0)" }, @@ -278,12 +259,11 @@ class VMwareHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter from where the nio should be removed", "port_number": "Port on the adapter (always 0)" }, @@ -303,8 +283,8 @@ class VMwareHandler: @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to start a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -327,8 +307,8 @@ class VMwareHandler: @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Adapter to stop a packet capture", "port_number": "Port on the adapter (always 0)" }, @@ -346,12 +326,11 @@ class VMwareHandler: yield from vm.stop_capture(adapter_number) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vmware/nodes/{node_id}/interfaces/vmnet", parameters={ - "project_id": "The UUID of the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 201: "VMnet interface allocated", @@ -366,3 +345,14 @@ class VMwareHandler: vm.vmnets.append(vmnet) response.set_status(201) response.json({"vmnet": vmnet}) + + @Route.get( + r"/vmware/vms", + status_codes={ + 200: "Success", + }, + description="Get all VMware VMs available") + def get_vms(request, response): + vmware_manager = VMware.instance() + vms = yield from vmware_manager.list_vms() + response.json(vms) diff --git a/gns3server/handlers/api/compute/vpcs_handler.py b/gns3server/handlers/api/compute/vpcs_handler.py index 9e540076..288e3d77 100644 --- a/gns3server/handlers/api/compute/vpcs_handler.py +++ b/gns3server/handlers/api/compute/vpcs_handler.py @@ -16,12 +16,15 @@ # along with this program. If not, see . from aiohttp.web import HTTPConflict -from ....web.route import Route -from ....schemas.nio import NIO_SCHEMA -from ....schemas.vpcs import VPCS_CREATE_SCHEMA -from ....schemas.vpcs import VPCS_UPDATE_SCHEMA -from ....schemas.vpcs import VPCS_OBJECT_SCHEMA -from ....compute.vpcs import VPCS +from gns3server.web.route import Route +from gns3server.schemas.nio import NIO_SCHEMA +from gns3server.compute.vpcs import VPCS + +from gns3server.schemas.vpcs import ( + VPCS_CREATE_SCHEMA, + VPCS_UPDATE_SCHEMA, + VPCS_OBJECT_SCHEMA +) class VPCSHandler: @@ -30,11 +33,10 @@ class VPCSHandler: API entry points for VPCS. """ - @classmethod @Route.post( r"/projects/{project_id}/vpcs/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -47,20 +49,19 @@ class VPCSHandler: def create(request, response): vpcs = VPCS.instance() - node = yield from vpcs.create_node(request.json["name"], - request.match_info["project_id"], - request.json.get("node_id"), - console=request.json.get("console"), - startup_script=request.json.get("startup_script")) + vm = yield from vpcs.create_node(request.json["name"], + request.match_info["project_id"], + request.json.get("node_id"), + console=request.json.get("console"), + startup_script=request.json.get("startup_script")) response.set_status(201) - response.json(node) + response.json(vm) - @classmethod @Route.get( r"/projects/{project_id}/vpcs/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Success", @@ -75,12 +76,11 @@ class VPCSHandler: vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) response.json(vm) - @classmethod @Route.put( r"/projects/{project_id}/vpcs/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 200: "Instance updated", @@ -100,12 +100,11 @@ class VPCSHandler: vm.startup_script = request.json.get("startup_script", vm.startup_script) response.json(vm) - @classmethod @Route.delete( r"/projects/{project_id}/vpcs/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance deleted", @@ -118,12 +117,11 @@ class VPCSHandler: yield from VPCS.instance().delete_node(request.match_info["node_id"]) response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vpcs/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance started", @@ -139,12 +137,11 @@ class VPCSHandler: yield from vm.start() response.json(vm) - @classmethod @Route.post( r"/projects/{project_id}/vpcs/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ 204: "Instance stopped", @@ -159,12 +156,11 @@ class VPCSHandler: yield from vm.stop() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/vpcs/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", }, status_codes={ 204: "Instance reloaded", @@ -182,8 +178,8 @@ class VPCSHandler: @Route.post( r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port where the nio should be added" }, @@ -207,12 +203,11 @@ class VPCSHandler: response.set_status(201) response.json(nio) - @classmethod @Route.delete( r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the instance", + "project_id": "Project UUID", + "node_id": "Node UUID", "adapter_number": "Network adapter where the nio is located", "port_number": "Port from where the nio should be removed" }, diff --git a/gns3server/handlers/api/controller/compute_handler.py b/gns3server/handlers/api/controller/compute_handler.py index 0037ea3c..57dc373e 100644 --- a/gns3server/handlers/api/controller/compute_handler.py +++ b/gns3server/handlers/api/controller/compute_handler.py @@ -18,27 +18,24 @@ import asyncio from aiohttp.web import HTTPForbidden -from ....web.route import Route -from ....config import Config -from ....compute.project_manager import ProjectManager -from ....schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA -from ....controller import Controller -from ....controller.compute import Compute - +from gns3server.web.route import Route +from gns3server.config import Config +from gns3server.compute.project_manager import ProjectManager +from gns3server.schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA +from gns3server.controller import Controller import logging log = logging.getLogger(__name__) class ComputeHandler: - """API entry points for compute management.""" + """API entry points for compute server management.""" - @classmethod @Route.post( r"/computes", - description="Register a compute", + description="Register a compute server", status_codes={ - 201: "Compute added" + 201: "Compute server added" }, input=COMPUTE_CREATE_SCHEMA, output=COMPUTE_OBJECT_SCHEMA) @@ -48,31 +45,29 @@ class ComputeHandler: response.set_status(201) response.json(compute) - @classmethod @Route.get( r"/computes", - description="List compute nodes", + description="List of compute server", status_codes={ - 200: "Compute list" + 200: "Compute servers list returned" }) def list(request, response): controller = Controller.instance() response.json([c for c in controller.computes.values()]) - @classmethod @Route.post( r"/computes/shutdown", - description="Shutdown the local compute", + description="Shutdown a local compute server", status_codes={ - 201: "Compute is shutting down", - 403: "Compute shutdown refused" + 201: "Compute server is shutting down", + 403: "Compute server shutdown refused" }) def shutdown(request, response): config = Config.instance() if config.get_section_config("Server").getboolean("local", False) is False: - raise HTTPForbidden(text="You can only stop a local server") + raise HTTPForbidden(text="Only a local server can be shutdown") # close all the projects first pm = ProjectManager.instance() @@ -97,12 +92,12 @@ class ComputeHandler: asyncio.async(server.shutdown_server()) response.set_status(201) - @classmethod + @Route.get( r"/computes/{compute_id:.+}", - description="Get a compute node informations", + description="Get a compute server information", status_codes={ - 200: "Compute list" + 200: "Compute server information returned" }, output=COMPUTE_OBJECT_SCHEMA) def get(request, response): diff --git a/gns3server/handlers/api/controller/link_handler.py b/gns3server/handlers/api/controller/link_handler.py index b38a356e..3491da1e 100644 --- a/gns3server/handlers/api/controller/link_handler.py +++ b/gns3server/handlers/api/controller/link_handler.py @@ -17,9 +17,13 @@ import aiohttp -from ....web.route import Route -from ....schemas.link import LINK_OBJECT_SCHEMA, LINK_CAPTURE_SCHEMA -from ....controller import Controller +from gns3server.web.route import Route +from gns3server.controller import Controller + +from gns3server.schemas.link import ( + LINK_OBJECT_SCHEMA, + LINK_CAPTURE_SCHEMA +) class LinkHandler: @@ -27,11 +31,10 @@ class LinkHandler: API entry point for Link """ - @classmethod @Route.post( r"/projects/{project_id}/links", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Link created", @@ -51,12 +54,11 @@ class LinkHandler: response.set_status(201) response.json(link) - @classmethod @Route.post( r"/projects/{project_id}/links/{link_id}/start_capture", parameters={ - "project_id": "UUID for the project", - "link_id": "UUID of the link" + "project_id": "Project UUID", + "link_id": "Link UUID" }, status_codes={ 201: "Capture started", @@ -64,7 +66,7 @@ class LinkHandler: }, input=LINK_CAPTURE_SCHEMA, output=LINK_OBJECT_SCHEMA, - description="Start capture on a link instance. By default we consider it as an ethernet link") + description="Start capture on a link instance. By default we consider it as an Ethernet link") def start_capture(request, response): controller = Controller.instance() @@ -74,12 +76,11 @@ class LinkHandler: response.set_status(201) response.json(link) - @classmethod @Route.post( r"/projects/{project_id}/links/{link_id}/stop_capture", parameters={ - "project_id": "UUID for the project", - "link_id": "UUID of the link" + "project_id": "Project UUID", + "link_id": "Link UUID" }, status_codes={ 201: "Capture stopped", @@ -95,12 +96,11 @@ class LinkHandler: response.set_status(201) response.json(link) - @classmethod @Route.delete( r"/projects/{project_id}/links/{link_id}", parameters={ - "project_id": "UUID for the project", - "link_id": "UUID of the link" + "project_id": "Project UUID", + "link_id": "Link UUID" }, status_codes={ 204: "Link deleted", @@ -116,16 +116,15 @@ class LinkHandler: response.set_status(204) response.json(link) - @classmethod @Route.get( r"/projects/{project_id}/links/{link_id}/pcap", parameters={ - "project_id": "UUID for the project", - "link_id": "UUID of the link" + "project_id": "Project UUID", + "link_id": "Link UUID" }, - description="Get the pcap from the capture", + description="Steam the pcap capture file", status_codes={ - 200: "Return the file", + 200: "File returned", 403: "Permission denied", 404: "The file doesn't exist" }) @@ -145,7 +144,7 @@ class LinkHandler: response.content_type = "application/vnd.tcpdump.pcap" response.set_status(200) response.enable_chunked_encoding() - # Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None response.start(request) diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py index 7bd6b579..da438204 100644 --- a/gns3server/handlers/api/controller/node_handler.py +++ b/gns3server/handlers/api/controller/node_handler.py @@ -15,9 +15,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ....web.route import Route -from ....schemas.node import NODE_OBJECT_SCHEMA, NODE_UPDATE_SCHEMA -from ....controller import Controller +from gns3server.web.route import Route +from gns3server.controller import Controller + +from gns3server.schemas.node import ( + NODE_OBJECT_SCHEMA, + NODE_UPDATE_SCHEMA +) class NodeHandler: @@ -25,11 +29,10 @@ class NodeHandler: API entry point for node """ - @classmethod @Route.post( r"/projects/{project_id}/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ 201: "Instance created", @@ -47,28 +50,27 @@ class NodeHandler: response.set_status(201) response.json(node) - @classmethod @Route.get( r"/projects/{project_id}/nodes", parameters={ - "project_id": "UUID for the project" + "project_id": "Project UUID" }, status_codes={ - 200: "List of nodes", + 200: "List of nodes returned", }, description="List nodes of a project") def list_nodes(request, response): controller = Controller.instance() project = controller.get_project(request.match_info["project_id"]) - response.json([ v for v in project.nodes.values() ]) + response.json([v for v in project.nodes.values()]) - @classmethod @Route.put( r"/projects/{project_id}/nodes/{node_id}", status_codes={ - 201: "Instance created", - 400: "Invalid request" + 200: "Instance updated", + 400: "Invalid request", + 404: "Instance doesn't exist" }, description="Update a node instance", input=NODE_UPDATE_SCHEMA, @@ -77,25 +79,25 @@ class NodeHandler: project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) - # Ignore this, because we use it only in create + # Ignore these because we only use them when creating a node request.json.pop("node_id", None) request.json.pop("node_type", None) request.json.pop("compute_id", None) yield from node.update(**request.json) - response.set_status(201) + response.set_status(200) response.json(node) - @classmethod @Route.post( r"/projects/{project_id}/nodes/{node_id}/start", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the node" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ - 201: "Instance created", - 400: "Invalid request" + 204: "Instance started", + 400: "Invalid request", + 404: "Instance doesn't exist" }, description="Start a node instance", output=NODE_OBJECT_SCHEMA) @@ -104,61 +106,58 @@ class NodeHandler: project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.start() - response.set_status(201) - response.json(node) + response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/nodes/{node_id}/stop", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the node" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ - 201: "Instance created", - 400: "Invalid request" + 204: "Instance stopped", + 400: "Invalid request", + 404: "Instance doesn't exist" }, - description="Start a node instance", + description="Stop a node instance", output=NODE_OBJECT_SCHEMA) def stop(request, response): project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.stop() - response.set_status(201) - response.json(node) + response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/nodes/{node_id}/suspend", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the node" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ - 201: "Instance created", - 400: "Invalid request" + 204: "Instance suspended", + 400: "Invalid request", + 404: "Instance doesn't exist" }, - description="Start a node instance", + description="Suspend a node instance", output=NODE_OBJECT_SCHEMA) def suspend(request, response): project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.suspend() - response.set_status(201) - response.json(node) + response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/nodes/{node_id}/reload", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the node" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ - 201: "Instance created", - 400: "Invalid request" + 204: "Instance reloaded", + 400: "Invalid request", + 404: "Instance doesn't exist" }, description="Reload a node instance", output=NODE_OBJECT_SCHEMA) @@ -167,23 +166,22 @@ class NodeHandler: project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.reload() - response.set_status(201) - response.json(node) + response.set_status(204) - @classmethod @Route.delete( r"/projects/{project_id}/nodes/{node_id}", parameters={ - "project_id": "UUID for the project", - "node_id": "UUID for the node" + "project_id": "Project UUID", + "node_id": "Node UUID" }, status_codes={ - 201: "Instance deleted", - 400: "Invalid request" + 204: "Instance deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" }, description="Delete a node instance") def delete(request, response): project = Controller.instance().get_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.destroy() - response.set_status(201) + response.set_status(204) diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index 35db544b..9f1e6b95 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -19,10 +19,13 @@ import aiohttp import asyncio -from ....web.route import Route -from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA -from ....controller import Controller +from gns3server.web.route import Route +from gns3server.controller import Controller +from gns3server.schemas.project import ( + PROJECT_OBJECT_SCHEMA, + PROJECT_CREATE_SCHEMA +) import logging log = logging.getLogger() @@ -30,7 +33,6 @@ log = logging.getLogger() class ProjectHandler: - @classmethod @Route.post( r"/projects", description="Create a new project on the server", @@ -43,15 +45,13 @@ class ProjectHandler: def create_project(request, response): controller = Controller.instance() - project = yield from controller.addProject( - name=request.json.get("name"), - path=request.json.get("path"), - project_id=request.json.get("project_id"), - temporary=request.json.get("temporary", False)) + project = yield from controller.add_project(name=request.json.get("name"), + path=request.json.get("path"), + project_id=request.json.get("project_id"), + temporary=request.json.get("temporary", False)) response.set_status(201) response.json(project) - @classmethod @Route.get( r"/projects", description="List projects", @@ -60,17 +60,16 @@ class ProjectHandler: }) def list_projects(request, response): controller = Controller.instance() - response.json([ p for p in controller.projects.values() ]) + response.json([p for p in controller.projects.values()]) - @classmethod @Route.get( r"/projects/{project_id}", - description="Get the project", + description="Get a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ - 200: "The project exist", + 200: "Project information returned", 404: "The project doesn't exist" }) def get(request, response): @@ -78,12 +77,11 @@ class ProjectHandler: project = controller.get_project(request.match_info["project_id"]) response.json(project) - @classmethod @Route.post( r"/projects/{project_id}/commit", description="Write changes on disk", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 204: "Changes have been written on disk", @@ -96,12 +94,11 @@ class ProjectHandler: yield from project.commit() response.set_status(204) - @classmethod @Route.post( r"/projects/{project_id}/close", description="Close a project", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 204: "The project has been closed", @@ -115,12 +112,11 @@ class ProjectHandler: controller.remove_project(project) response.set_status(204) - @classmethod @Route.delete( r"/projects/{project_id}", description="Delete a project from disk", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 204: "Changes have been written on disk", @@ -134,12 +130,11 @@ class ProjectHandler: controller.remove_project(project) response.set_status(204) - @classmethod @Route.get( r"/projects/{project_id}/notifications", - description="Receive notifications about the projects", + description="Receive notifications about projects", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 200: "End of stream", @@ -153,7 +148,7 @@ class ProjectHandler: response.content_type = "application/json" response.set_status(200) response.enable_chunked_encoding() - # Very important: do not send a content lenght otherwise QT close the connection but curl can consume the Feed + # Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed) response.content_length = None response.start(request) @@ -165,12 +160,11 @@ class ProjectHandler: except asyncio.futures.CancelledError as e: break - @classmethod @Route.get( r"/projects/{project_id}/notifications/ws", - description="Receive notifications about the projects via Websocket", + description="Receive notifications about projects from a Websocket", parameters={ - "project_id": "The UUID of the project", + "project_id": "Project UUID", }, status_codes={ 200: "End of stream", @@ -187,8 +181,8 @@ class ProjectHandler: with project.queue() as queue: while True: try: - notif = yield from queue.get_json(5) + notification = yield from queue.get_json(5) except asyncio.futures.CancelledError as e: break - ws.send_str(notif) + ws.send_str(notification) return ws diff --git a/gns3server/handlers/api/controller/version_handler.py b/gns3server/handlers/api/controller/version_handler.py index dad6d31b..b45f4aa9 100644 --- a/gns3server/handlers/api/controller/version_handler.py +++ b/gns3server/handlers/api/controller/version_handler.py @@ -15,16 +15,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ....web.route import Route -from ....config import Config -from ....schemas.version import VERSION_SCHEMA -from ....version import __version__ +from gns3server.web.route import Route +from gns3server.config import Config +from gns3server.schemas.version import VERSION_SCHEMA +from gns3server.version import __version__ from aiohttp.web import HTTPConflict class VersionHandler: - @classmethod @Route.get( r"/version", description="Retrieve the server version number", @@ -35,7 +34,6 @@ class VersionHandler: local_server = config.get_section_config("Server").getboolean("local", False) response.json({"version": __version__, "local": local_server}) - @classmethod @Route.post( r"/version", description="Check if version is the same as the server", diff --git a/gns3server/handlers/index_handler.py b/gns3server/handlers/index_handler.py index 2aa42cea..14302e18 100644 --- a/gns3server/handlers/index_handler.py +++ b/gns3server/handlers/index_handler.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2015 GNS3 Technologies Inc. +# Copyright (C) 2016 GNS3 Technologies Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,58 +15,51 @@ # along with this program. If not, see . -from ..web.route import Route -from ..controller import Controller -from ..compute.port_manager import PortManager -from ..compute.project_manager import ProjectManager -from ..version import __version__ +from gns3server.web.route import Route +from gns3server.controller import Controller +from gns3server.compute.port_manager import PortManager +from gns3server.compute.project_manager import ProjectManager +from gns3server.version import __version__ class IndexHandler: - @classmethod @Route.get( r"/", - description="Home page for GNS3Server" + description="Home page of the GNS3 server" ) def index(request, response): response.template("index.html") - @classmethod @Route.get( r"/compute", - description="Ressources used by GNS3 Compute" + description="Resources used by the GNS3 compute servers" ) def compute(request, response): response.template("compute.html", port_manager=PortManager.instance(), - project_manager=ProjectManager.instance() - ) + project_manager=ProjectManager.instance()) - @classmethod @Route.get( r"/controller", - description="Ressources used by GNS3 Controller" + description="Resources used by the GNS3 controller server" ) def controller(request, response): response.template("controller.html", - controller=Controller.instance() - ) + controller=Controller.instance()) - @classmethod @Route.get( r"/projects/{project_id}", - description="Ressources used by GNS3 Controller" + description="List of the GNS3 projects" ) def project(request, response): controller = Controller.instance() response.template("project.html", project=controller.get_project(request.match_info["project_id"])) - @classmethod @Route.get( r"/v1/version", - description="Old API" + description="Old 1.0 API" ) def get_v1(request, response): response.json({"version": __version__}) diff --git a/gns3server/handlers/upload_handler.py b/gns3server/handlers/upload_handler.py index a0526950..be9ee137 100644 --- a/gns3server/handlers/upload_handler.py +++ b/gns3server/handlers/upload_handler.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2015 GNS3 Technologies Inc. +# Copyright (C) 2016 GNS3 Technologies Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,17 +22,16 @@ import io import tarfile import asyncio -from ..config import Config -from ..web.route import Route -from ..utils.images import remove_checksum, md5sum +from gns3server.config import Config +from gns3server.web.route import Route +from gns3server.utils.images import remove_checksum, md5sum class UploadHandler: - @classmethod @Route.get( r"/upload", - description="Manage upload of GNS3 images", + description="List binary images", api_version=None ) def index(request, response): @@ -50,10 +49,9 @@ class UploadHandler: uploaded_files.append(iourc_path) response.template("upload.html", files=uploaded_files) - @classmethod @Route.post( r"/upload", - description="Manage upload of GNS3 images", + description="Upload binary images", api_version=None ) def upload(request, response): @@ -95,16 +93,14 @@ class UploadHandler: return response.redirect("/upload") - @classmethod @Route.get( r"/backup/images.tar", - description="Backup GNS3 images", + description="Backup binary images", api_version=None ) def backup_images(request, response): yield from UploadHandler._backup_directory(request, response, UploadHandler.image_directory()) - @classmethod @Route.get( r"/backup/projects.tar", description="Backup GNS3 projects", diff --git a/gns3server/schemas/node.py b/gns3server/schemas/node.py index dd2c597c..e0fbf627 100644 --- a/gns3server/schemas/node.py +++ b/gns3server/schemas/node.py @@ -18,7 +18,7 @@ NODE_LIST_IMAGES_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", - "description": "List of disk images", + "description": "List of binary images", "type": "array", "items": [ { diff --git a/tests/compute/virtualbox/test_virtualbox_manager.py b/tests/compute/virtualbox/test_virtualbox_manager.py index fffe2c78..c3ca7a4f 100644 --- a/tests/compute/virtualbox/test_virtualbox_manager.py +++ b/tests/compute/virtualbox/test_virtualbox_manager.py @@ -71,7 +71,7 @@ def test_vboxmanage_path(manager, tmpdir): assert manager.find_vboxmanage() == path -def test_list_images(manager, loop): +def test_list_vms(manager, loop): vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', '"Carriage', 'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', @@ -92,7 +92,7 @@ def test_list_images(manager, loop): with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute") as mock: mock.side_effect = execute_mock - vms = loop.run_until_complete(asyncio.async(manager.list_images())) + vms = loop.run_until_complete(asyncio.async(manager.list_vms())) assert vms == [ {"vmname": "Windows 8.1", "ram": 512}, {"vmname": "Linux Microcore 4.7.1", "ram": 256} diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index d3f5c5d3..5afecb38 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -121,18 +121,18 @@ def test_addProject(controller, async_run): uuid1 = str(uuid.uuid4()) uuid2 = str(uuid.uuid4()) - async_run(controller.addProject(project_id=uuid1)) + async_run(controller.add_project(project_id=uuid1)) assert len(controller.projects) == 1 - async_run(controller.addProject(project_id=uuid1)) + async_run(controller.add_project(project_id=uuid1)) assert len(controller.projects) == 1 - async_run(controller.addProject(project_id=uuid2)) + async_run(controller.add_project(project_id=uuid2)) assert len(controller.projects) == 2 def test_remove_project(controller, async_run): uuid1 = str(uuid.uuid4()) - project1 = async_run(controller.addProject(project_id=uuid1)) + project1 = async_run(controller.add_project(project_id=uuid1)) assert len(controller.projects) == 1 controller.remove_project(project1) @@ -146,13 +146,13 @@ def test_addProject_with_compute(controller, async_run): compute.post = MagicMock() controller._computes = {"test1": compute} - project1 = async_run(controller.addProject(project_id=uuid1)) + project1 = async_run(controller.add_project(project_id=uuid1)) def test_getProject(controller, async_run): uuid1 = str(uuid.uuid4()) - project = async_run(controller.addProject(project_id=uuid1)) + project = async_run(controller.add_project(project_id=uuid1)) assert controller.get_project(uuid1) == project with pytest.raises(aiohttp.web.HTTPNotFound): assert controller.get_project("dsdssd") diff --git a/tests/handlers/api/compute/test_docker.py b/tests/handlers/api/compute/test_docker.py index 61173e5f..a981dac9 100644 --- a/tests/handlers/api/compute/test_docker.py +++ b/tests/handlers/api/compute/test_docker.py @@ -96,9 +96,16 @@ def test_docker_delete(http_compute, vm): assert response.status == 204 -def test_docker_reload(http_compute, vm): +def test_docker_pause(http_compute, vm): with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock: - response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) + response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/pause".format(project_id=vm["project_id"], node_id=vm["node_id"])) + assert mock.called + assert response.status == 204 + + +def test_docker_unpause(http_compute, vm): + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock: + response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/unpause".format(project_id=vm["project_id"], node_id=vm["node_id"])) assert mock.called assert response.status == 204 diff --git a/tests/handlers/api/compute/test_dynamips.py b/tests/handlers/api/compute/test_dynamips.py index f494d1a1..30654c37 100644 --- a/tests/handlers/api/compute/test_dynamips.py +++ b/tests/handlers/api/compute/test_dynamips.py @@ -151,17 +151,17 @@ def fake_file(tmpdir): return path -def test_vms(http_compute, tmpdir, fake_dynamips, fake_file): +def test_images(http_compute, tmpdir, fake_dynamips, fake_file): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir), example=True): - response = http_compute.get("/dynamips/nodes") + response = http_compute.get("/dynamips/images") assert response.status == 200 assert response.json == [{"filename": "7200.bin", "path": "7200.bin"}] -def test_upload_vm(http_compute, tmpdir): +def test_upload_image(http_compute, tmpdir): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/dynamips/nodes/test2", body="TEST", raw=True) + response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True) assert response.status == 204 with open(str(tmpdir / "test2")) as f: @@ -172,11 +172,11 @@ def test_upload_vm(http_compute, tmpdir): assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" -def test_upload_vm_permission_denied(http_compute, tmpdir): +def test_upload_image_permission_denied(http_compute, tmpdir): with open(str(tmpdir / "test2.tmp"), "w+") as f: f.write("") os.chmod(str(tmpdir / "test2.tmp"), 0) with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/dynamips/nodes/test2", body="TEST", raw=True) + response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True) assert response.status == 409 diff --git a/tests/handlers/api/compute/test_iou.py b/tests/handlers/api/compute/test_iou.py index d92e32c8..12b6f361 100644 --- a/tests/handlers/api/compute/test_iou.py +++ b/tests/handlers/api/compute/test_iou.py @@ -328,17 +328,17 @@ def test_get_configs_with_startup_config_file(http_compute, project, vm): assert response.json["startup_config_content"] == "TEST" -def test_vms(http_compute, tmpdir, fake_iou_bin): +def test_images(http_compute, tmpdir, fake_iou_bin): with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)): - response = http_compute.get("/iou/nodes", example=True) + response = http_compute.get("/iou/images", example=True) assert response.status == 200 assert response.json == [{"filename": "iou.bin", "path": "iou.bin"}] -def test_upload_vm(http_compute, tmpdir): +def test_image_vm(http_compute, tmpdir): with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/iou/nodes/test2", body="TEST", raw=True) + response = http_compute.post("/iou/images/test2", body="TEST", raw=True) assert response.status == 204 with open(str(tmpdir / "test2")) as f: diff --git a/tests/handlers/api/compute/test_qemu.py b/tests/handlers/api/compute/test_qemu.py index 3046efcc..9459a2b9 100644 --- a/tests/handlers/api/compute/test_qemu.py +++ b/tests/handlers/api/compute/test_qemu.py @@ -224,16 +224,16 @@ def test_qemu_list_binaries_filter(http_compute, vm): assert response.json == ret -def test_vms(http_compute, tmpdir, fake_qemu_vm): +def test_images(http_compute, tmpdir, fake_qemu_vm): - response = http_compute.get("/qemu/nodes") + response = http_compute.get("/qemu/images") assert response.status == 200 assert response.json == [{"filename": "linux载.img", "path": "linux载.img"}] -def test_upload_vm(http_compute, tmpdir): +def test_upload_image(http_compute, tmpdir): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/qemu/nodes/test2", body="TEST", raw=True) + response = http_compute.post("/qemu/images/test2", body="TEST", raw=True) assert response.status == 204 with open(str(tmpdir / "test2")) as f: @@ -244,9 +244,9 @@ def test_upload_vm(http_compute, tmpdir): assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" -def test_upload_vm_ova(http_compute, tmpdir): +def test_upload_image_ova(http_compute, tmpdir): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/qemu/nodes/test2.ova/test2.vmdk", body="TEST", raw=True) + response = http_compute.post("/qemu/images/test2.ova/test2.vmdk", body="TEST", raw=True) assert response.status == 204 with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: @@ -257,19 +257,19 @@ def test_upload_vm_ova(http_compute, tmpdir): assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" -def test_upload_vm_forbiden_location(http_compute, tmpdir): +def test_upload_image_forbiden_location(http_compute, tmpdir): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/qemu/nodes/../../test2", body="TEST", raw=True) + response = http_compute.post("/qemu/images/../../test2", body="TEST", raw=True) assert response.status == 403 -def test_upload_vm_permission_denied(http_compute, tmpdir): +def test_upload_image_permission_denied(http_compute, tmpdir): with open(str(tmpdir / "test2.tmp"), "w+") as f: f.write("") os.chmod(str(tmpdir / "test2.tmp"), 0) with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),): - response = http_compute.post("/qemu/nodes/test2", body="TEST", raw=True) + response = http_compute.post("/qemu/images/test2", body="TEST", raw=True) assert response.status == 409 diff --git a/tests/handlers/api/controller/test_link.py b/tests/handlers/api/controller/test_link.py index f77c3e1e..8e362d10 100644 --- a/tests/handlers/api/controller/test_link.py +++ b/tests/handlers/api/controller/test_link.py @@ -45,7 +45,7 @@ def compute(http_controller, async_run): @pytest.fixture def project(http_controller, async_run): - return async_run(Controller.instance().addProject()) + return async_run(Controller.instance().add_project()) def test_create_link(http_controller, tmpdir, project, compute, async_run): diff --git a/tests/handlers/api/controller/test_node.py b/tests/handlers/api/controller/test_node.py index 4d7229dd..12f941af 100644 --- a/tests/handlers/api/controller/test_node.py +++ b/tests/handlers/api/controller/test_node.py @@ -44,7 +44,7 @@ def compute(http_controller, async_run): @pytest.fixture def project(http_controller, async_run): - return async_run(Controller.instance().addProject()) + return async_run(Controller.instance().add_project()) @pytest.fixture @@ -103,7 +103,7 @@ def test_update_node(http_controller, tmpdir, project, compute, node): "startup_script": "echo test" } }, example=True) - assert response.status == 201 + assert response.status == 200 assert response.json["name"] == "test" assert "name" not in response.json["properties"] @@ -113,17 +113,14 @@ def test_start_node(http_controller, tmpdir, project, compute, node): compute.post = AsyncioMagicMock() response = http_controller.post("/projects/{}/nodes/{}/start".format(project.id, node.id), example=True) - assert response.status == 201 - assert response.json["name"] == node.name - + assert response.status == 204 def test_stop_node(http_controller, tmpdir, project, compute, node): response = MagicMock() compute.post = AsyncioMagicMock() response = http_controller.post("/projects/{}/nodes/{}/stop".format(project.id, node.id), example=True) - assert response.status == 201 - assert response.json["name"] == node.name + assert response.status == 204 def test_suspend_node(http_controller, tmpdir, project, compute, node): @@ -131,8 +128,7 @@ def test_suspend_node(http_controller, tmpdir, project, compute, node): compute.post = AsyncioMagicMock() response = http_controller.post("/projects/{}/nodes/{}/suspend".format(project.id, node.id), example=True) - assert response.status == 201 - assert response.json["name"] == node.name + assert response.status == 204 def test_reload_node(http_controller, tmpdir, project, compute, node): @@ -140,8 +136,7 @@ def test_reload_node(http_controller, tmpdir, project, compute, node): compute.post = AsyncioMagicMock() response = http_controller.post("/projects/{}/nodes/{}/reload".format(project.id, node.id), example=True) - assert response.status == 201 - assert response.json["name"] == node.name + assert response.status == 204 def test_delete_node(http_controller, tmpdir, project, compute, node): @@ -149,4 +144,4 @@ def test_delete_node(http_controller, tmpdir, project, compute, node): compute.post = AsyncioMagicMock() response = http_controller.delete("/projects/{}/nodes/{}".format(project.id, node.id), example=True) - assert response.status == 201 + assert response.status == 204 diff --git a/tests/handlers/test_index.py b/tests/handlers/test_index.py index eaa7f22a..385940f0 100644 --- a/tests/handlers/test_index.py +++ b/tests/handlers/test_index.py @@ -33,7 +33,7 @@ def test_index(http_root): def test_controller(http_root, async_run): - project = async_run(Controller.instance().addProject(name="test")) + project = async_run(Controller.instance().add_project(name="test")) response = http_root.get('/controller') assert "test" in response.html assert response.status == 200 @@ -45,7 +45,7 @@ def test_compute(http_root): def test_project(http_root, async_run): - project = async_run(Controller.instance().addProject(name="test")) + project = async_run(Controller.instance().add_project(name="test")) response = http_root.get('/projects/{}'.format(project.id)) assert response.status == 200