From ab60d7929b33fa7b71fa5df158a86af5356e7a4b Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 30 Apr 2015 19:05:37 -0600 Subject: [PATCH 001/336] Basic VMware support (start & stop a VM). --- gns3server/handlers/__init__.py | 1 + gns3server/handlers/api/virtualbox_handler.py | 2 +- gns3server/handlers/api/vmware_handler.py | 229 ++++++++++++++++++ gns3server/modules/__init__.py | 3 +- gns3server/modules/vmware/__init__.py | 106 ++++++++ gns3server/modules/vmware/vmware_error.py | 27 +++ gns3server/modules/vmware/vmware_vm.py | 192 +++++++++++++++ gns3server/schemas/vmware.py | 143 +++++++++++ 8 files changed, 701 insertions(+), 2 deletions(-) create mode 100644 gns3server/handlers/api/vmware_handler.py create mode 100644 gns3server/modules/vmware/__init__.py create mode 100644 gns3server/modules/vmware/vmware_error.py create mode 100644 gns3server/modules/vmware/vmware_vm.py create mode 100644 gns3server/schemas/vmware.py diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py index 842bb960..68297c25 100644 --- a/gns3server/handlers/__init__.py +++ b/gns3server/handlers/__init__.py @@ -25,6 +25,7 @@ from gns3server.handlers.api.dynamips_vm_handler import DynamipsVMHandler from gns3server.handlers.api.qemu_handler import QEMUHandler from gns3server.handlers.api.virtualbox_handler import VirtualBoxHandler from gns3server.handlers.api.vpcs_handler import VPCSHandler +from gns3server.handlers.api.vmware_handler import VMwareHandler from gns3server.handlers.api.config_handler import ConfigHandler from gns3server.handlers.api.server_handler import ServerHandler from gns3server.handlers.api.file_handler import FileHandler diff --git a/gns3server/handlers/api/virtualbox_handler.py b/gns3server/handlers/api/virtualbox_handler.py index 17d6ce66..f948fbe4 100644 --- a/gns3server/handlers/api/virtualbox_handler.py +++ b/gns3server/handlers/api/virtualbox_handler.py @@ -246,7 +246,7 @@ class VirtualBoxHandler: 404: "Instance doesn't exist" }, description="Resume a suspended VirtualBox VM instance") - def suspend(request, response): + def resume(request, response): vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) diff --git a/gns3server/handlers/api/vmware_handler.py b/gns3server/handlers/api/vmware_handler.py new file mode 100644 index 00000000..e8bf35e0 --- /dev/null +++ b/gns3server/handlers/api/vmware_handler.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +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 ...modules.vmware import VMware +from ...modules.project_manager import ProjectManager + + +class VMwareHandler: + + """ + API entry points for VMware. + """ + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms", + parameters={ + "project_id": "UUID for the project" + }, + status_codes={ + 201: "Instance created", + 400: "Invalid request", + 409: "Conflict" + }, + description="Create a new VMware VM instance", + input=VMWARE_CREATE_SCHEMA, + output=VMWARE_OBJECT_SCHEMA) + def create(request, response): + + vmware_manager = VMware.instance() + vm = yield from vmware_manager.create_vm(request.json.pop("name"), + request.match_info["project_id"], + request.json.get("vm_id"), + request.json.pop("vmx_path"), + request.json.pop("linked_clone"), + console=request.json.get("console", None)) + + # for name, value in request.json.items(): + # if name != "vm_id": + # if hasattr(vm, name) and getattr(vm, name) != value: + # setattr(vm, name, value) + + response.set_status(201) + response.json(vm) + + @classmethod + @Route.get( + r"/projects/{project_id}/vmware/vms/{vm_id}", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 200: "Success", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Get a VMware VM instance", + output=VMWARE_OBJECT_SCHEMA) + def show(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + response.json(vm) + + @classmethod + @Route.put( + r"/projects/{project_id}/vmware/vms/{vm_id}", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 200: "Instance updated", + 400: "Invalid request", + 404: "Instance doesn't exist", + 409: "Conflict" + }, + description="Update a VMware VM instance", + input=VMWARE_UPDATE_SCHEMA, + output=VMWARE_OBJECT_SCHEMA) + def update(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + + # for name, value in request.json.items(): + # if hasattr(vm, name) and getattr(vm, name) != value: + # setattr(vm, name, value) + + response.json(vm) + + @classmethod + @Route.delete( + r"/projects/{project_id}/vmware/vms/{vm_id}", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Delete a VMware VM instance") + def delete(request, response): + + # check the project_id exists + ProjectManager.instance().get_project(request.match_info["project_id"]) + yield from VMware.instance().delete_vm(request.match_info["vm_id"]) + response.set_status(204) + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/start", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance started", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Start a VMware VM instance") + def start(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + yield from vm.start() + response.set_status(204) + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/stop", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance stopped", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Stop a VMware VM instance") + def stop(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + yield from vm.stop() + response.set_status(204) + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/suspend", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance suspended", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Suspend a VMware VM instance") + def suspend(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + yield from vm.suspend() + response.set_status(204) + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/resume", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance resumed", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Resume a suspended VMware VM instance") + def resume(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + yield from vm.resume() + response.set_status(204) + + @classmethod + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/reload", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance" + }, + status_codes={ + 204: "Instance reloaded", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Reload a VMware VM instance") + def reload(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + yield from vm.reload() + response.set_status(204) diff --git a/gns3server/modules/__init__.py b/gns3server/modules/__init__.py index 928bb1a9..7c23503c 100644 --- a/gns3server/modules/__init__.py +++ b/gns3server/modules/__init__.py @@ -21,8 +21,9 @@ from .vpcs import VPCS from .virtualbox import VirtualBox from .dynamips import Dynamips from .qemu import Qemu +from .vmware import VMware -MODULES = [VPCS, VirtualBox, Dynamips, Qemu] +MODULES = [VPCS, VirtualBox, Dynamips, Qemu, VMware] if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test") or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1": diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py new file mode 100644 index 00000000..6d876430 --- /dev/null +++ b/gns3server/modules/vmware/__init__.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +VMware player/workstation server module. +""" + +import os +import sys +import shutil +import asyncio +import subprocess +import logging + +log = logging.getLogger(__name__) + +from ..base_manager import BaseManager +from .vmware_vm import VMwareVM +from .vmware_error import VMwareError + + +class VMware(BaseManager): + + _VM_CLASS = VMwareVM + + def __init__(self): + + super().__init__() + self._vmrun_path = None + self._host_type = "player" + + @property + def vmrun_path(self): + """ + Returns the path vmrun utility. + + :returns: path + """ + + return self._vmrun_path + + def find_vmrun(self): + + # look for vmrun + vmrun_path = self.config.get_section_config("VMware").get("vmrun_path") + if not vmrun_path: + if sys.platform.startswith("win"): + pass # TODO: use registry to find vmrun + elif sys.platform.startswith("darwin"): + vmrun_path = "/Applications/VMware Fusion.app/Contents/Library/vmrun" + else: + vmrun_path = shutil.which("vmrun") + + if not vmrun_path: + raise VMwareError("Could not find vmrun") + if not os.path.isfile(vmrun_path): + raise VMwareError("vmrun {} is not accessible".format(vmrun_path)) + if not os.access(vmrun_path, os.X_OK): + raise VMwareError("vmrun is not executable") + if os.path.basename(vmrun_path) not in ["vmrun", "vmrun.exe"]: + raise VMwareError("Invalid vmrun executable name {}".format(os.path.basename(vmrun_path))) + + self._vmrun_path = vmrun_path + return vmrun_path + + @asyncio.coroutine + def execute(self, subcommand, args, timeout=60, host_type=None): + + vmrun_path = self.vmrun_path + if not vmrun_path: + vmrun_path = self.find_vmrun() + if host_type is None: + host_type = self._host_type + command = [vmrun_path, "-T", host_type, subcommand] + command.extend(args) + log.debug("Executing vmrun with command: {}".format(command)) + try: + process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + except (OSError, subprocess.SubprocessError) as e: + raise VMwareError("Could not execute vmrun: {}".format(e)) + + try: + stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout) + except asyncio.TimeoutError: + raise VMwareError("vmrun has timed out after {} seconds!".format(timeout)) + + if process.returncode: + # vmrun print errors on stdout + vmrun_error = stdout_data.decode("utf-8", errors="ignore") + raise VMwareError("vmrun has returned an error: {}".format(vmrun_error)) + + return stdout_data.decode("utf-8", errors="ignore").splitlines() diff --git a/gns3server/modules/vmware/vmware_error.py b/gns3server/modules/vmware/vmware_error.py new file mode 100644 index 00000000..8a254030 --- /dev/null +++ b/gns3server/modules/vmware/vmware_error.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Custom exceptions for the VirtualBox module. +""" + +from ..vm_error import VMError + + +class VMwareError(VMError): + + pass diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py new file mode 100644 index 00000000..1bc1feee --- /dev/null +++ b/gns3server/modules/vmware/vmware_vm.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2014 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +VMware VM instance. +""" + +import sys +import shlex +import re +import os +import tempfile +import json +import socket +import asyncio + +from pkg_resources import parse_version +from .vmware_error import VMwareError +from ..nios.nio_udp import NIOUDP +from ..base_vm import BaseVM + +import logging +log = logging.getLogger(__name__) + + +class VMwareVM(BaseVM): + + """ + VMware VM implementation. + """ + + def __init__(self, name, vm_id, project, manager, vmx_path, linked_clone, console=None): + + super().__init__(name, vm_id, project, manager, console=console) + + self._linked_clone = linked_clone + self._closed = False + + # VMware VM settings + self._headless = False + self._vmx_path = vmx_path + + def __json__(self): + + return {"name": self.name, + "vm_id": self.id, + "console": self.console, + "project_id": self.project.id, + "vmx_path": self.vmx_path, + "headless": self.headless} + + @asyncio.coroutine + def _control_vm(self, subcommand, *additional_args): + + args = [self._vmx_path] + args.extend(additional_args) + result = yield from self.manager.execute(subcommand, args) + log.debug("Control VM '{}' result: {}".format(subcommand, result)) + return result + + @asyncio.coroutine + def start(self): + """ + Starts this VMware VM. + """ + + if self._headless: + yield from self._control_vm("start", "nogui") + else: + yield from self._control_vm("start") + log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id)) + + @asyncio.coroutine + def stop(self): + """ + Stops this VMware VM. + """ + + yield from self._control_vm("stop") + log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) + + @asyncio.coroutine + def suspend(self): + """ + Suspends this VMware VM. + """ + + yield from self._control_vm("suspend") + log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) + + @asyncio.coroutine + def resume(self): + """ + Resumes this VMware VM. + """ + + yield from self.start() + log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id)) + + @asyncio.coroutine + def reload(self): + """ + Reloads this VMware VM. + """ + + yield from self._control_vm("reset") + log.info("VMware VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id)) + + @asyncio.coroutine + def close(self): + """ + Closes this VirtualBox VM. + """ + + if self._closed: + # VM is already closed + return + + log.debug("VMware VM '{name}' [{id}] is closing".format(name=self.name, id=self.id)) + if self._console: + self._manager.port_manager.release_tcp_port(self._console, self._project) + self._console = None + + #for adapter in self._ethernet_adapters.values(): + # if adapter is not None: + # for nio in adapter.ports.values(): + # if nio and isinstance(nio, NIOUDP): + # self.manager.port_manager.release_udp_port(nio.lport, self._project) + + yield from self.stop() + + log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id)) + self._closed = True + + @property + def headless(self): + """ + Returns either the VM will start in headless mode + + :returns: boolean + """ + + return self._headless + + @headless.setter + def headless(self, headless): + """ + Sets either the VM will start in headless mode + + :param headless: boolean + """ + + if headless: + log.info("VMware VM '{name}' [{id}] has enabled the headless mode".format(name=self.name, id=self.id)) + else: + log.info("VMware VM '{name}' [{id}] has disabled the headless mode".format(name=self.name, id=self.id)) + self._headless = headless + + @property + def vmx_path(self): + """ + Returns the path to the vmx file. + + :returns: VMware vmx file + """ + + return self._vmx_path + + @vmx_path.setter + def vmx_path(self, vmx_path): + """ + Sets the path to the vmx file. + + :param vmx_path: VMware vmx file + """ + + log.info("VMware VM '{name}' [{id}] has set the vmx file path to '{vmx}'".format(name=self.name, id=self.id, vmx=vmx_path)) + self._vmx_path = vmx_path diff --git a/gns3server/schemas/vmware.py b/gns3server/schemas/vmware.py new file mode 100644 index 00000000..959bef5b --- /dev/null +++ b/gns3server/schemas/vmware.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2014 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +VMWARE_CREATE_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation to create a new VMware VM instance", + "type": "object", + "properties": { + "vm_id": { + "description": "VMware VM instance identifier", + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "linked_clone": { + "description": "either the VM is a linked clone or not", + "type": "boolean" + }, + "name": { + "description": "VMware VM instance name", + "type": "string", + "minLength": 1, + }, + "vmx_path": { + "description": "path to the vmx file", + "type": "string", + "minLength": 1, + }, + "console": { + "description": "console TCP port", + "minimum": 1, + "maximum": 65535, + "type": "integer" + }, + "enable_remote_console": { + "description": "enable the remote console", + "type": "boolean" + }, + "headless": { + "description": "headless mode", + "type": "boolean" + }, + }, + "additionalProperties": False, + "required": ["name", "vmx_path", "linked_clone"], +} + +VMWARE_UPDATE_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation to update a VMware VM instance", + "type": "object", + "properties": { + "name": { + "description": "VMware VM instance name", + "type": "string", + "minLength": 1, + }, + "vmx_path": { + "description": "path to the vmx file", + "type": "string", + "minLength": 1, + }, + "console": { + "description": "console TCP port", + "minimum": 1, + "maximum": 65535, + "type": "integer" + }, + "enable_remote_console": { + "description": "enable the remote console", + "type": "boolean" + }, + "headless": { + "description": "headless mode", + "type": "boolean" + }, + }, + "additionalProperties": False, +} + +VMWARE_OBJECT_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "VMware VM instance", + "type": "object", + "properties": { + "name": { + "description": "VMware VM instance name", + "type": "string", + "minLength": 1, + }, + "vm_id": { + "description": "VMware VM instance UUID", + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "project_id": { + "description": "Project UUID", + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "vmx_path": { + "description": "path to the vmx file", + "type": "string", + "minLength": 1, + }, + "enable_remote_console": { + "description": "enable the remote console", + "type": "boolean" + }, + "headless": { + "description": "headless mode", + "type": "boolean" + }, + "console": { + "description": "console TCP port", + "minimum": 1, + "maximum": 65535, + "type": "integer" + }, + }, + "additionalProperties": False, + "required": ["name", "vm_id", "project_id"] +} From e6eab1fb25de09a989cfaf579932826ac1e5f14a Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 30 Apr 2015 22:48:27 +0200 Subject: [PATCH 002/336] Aiohttp 0.15.1 Conflicts: requirements.txt setup.py --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index c12c2071..26ea3a4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ netifaces==0.10.4 jsonschema==2.4.0 python-dateutil==2.3 -aiohttp==0.14.4 +aiohttp==0.15.1 Jinja2==2.7.3 raven==5.2.0 diff --git a/setup.py b/setup.py index f72b781d..01b853e8 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ class PyTest(TestCommand): sys.exit(errcode) -dependencies = ["aiohttp>=0.14.4", +dependencies = ["aiohttp>=0.15.1", "jsonschema>=2.4.0", "Jinja2>=2.7.3", "raven>=5.2.0"] From b3f2a6ac2af4f65f98716491df283edd17dbf322 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 1 May 2015 11:20:55 +0200 Subject: [PATCH 003/336] Fix tests and build documentation --- .../api/examples/delete_projectsprojectid.txt | 2 +- .../delete_projectsprojectidiouvmsvmid.txt | 6 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- .../delete_projectsprojectidqemuvmsvmid.txt | 6 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- .../delete_projectsprojectidvpcsvmsvmid.txt | 6 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- docs/api/examples/get_interfaces.txt | 10 +- docs/api/examples/get_projectsprojectid.txt | 6 +- .../get_projectsprojectidiouvmsvmid.txt | 12 +-- ...ojectsprojectidiouvmsvmidinitialconfig.txt | 6 +- .../get_projectsprojectidqemuvmsvmid.txt | 10 +- ...get_projectsprojectidvirtualboxvmsvmid.txt | 8 +- .../get_projectsprojectidvpcsvmsvmid.txt | 8 +- docs/api/examples/get_qemubinaries.txt | 2 +- docs/api/examples/get_version.txt | 6 +- docs/api/examples/post_configreload.txt | 2 +- docs/api/examples/post_projects.txt | 8 +- .../examples/post_projectsprojectidclose.txt | 2 +- .../examples/post_projectsprojectidcommit.txt | 2 +- .../examples/post_projectsprojectidiouvms.txt | 32 +++---- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- ...ternumberdportsportnumberdstartcapture.txt | 8 +- ...pternumberdportsportnumberdstopcapture.txt | 6 +- ...post_projectsprojectidiouvmsvmidreload.txt | 6 +- .../post_projectsprojectidiouvmsvmidstart.txt | 6 +- .../post_projectsprojectidiouvmsvmidstop.txt | 6 +- .../post_projectsprojectidportsudp.txt | 2 +- .../post_projectsprojectidqemuvms.txt | 10 +- ...ptersadapternumberdportsportnumberdnio.txt | 14 +-- ...ost_projectsprojectidqemuvmsvmidreload.txt | 6 +- ...ost_projectsprojectidqemuvmsvmidresume.txt | 6 +- ...post_projectsprojectidqemuvmsvmidstart.txt | 6 +- .../post_projectsprojectidqemuvmsvmidstop.txt | 6 +- ...st_projectsprojectidqemuvmsvmidsuspend.txt | 6 +- .../post_projectsprojectidvirtualboxvms.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- ...ojectsprojectidvirtualboxvmsvmidreload.txt | 6 +- ...ojectsprojectidvirtualboxvmsvmidresume.txt | 6 +- ...rojectsprojectidvirtualboxvmsvmidstart.txt | 6 +- ...projectsprojectidvirtualboxvmsvmidstop.txt | 6 +- ...jectsprojectidvirtualboxvmsvmidsuspend.txt | 6 +- .../post_projectsprojectidvpcsvms.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 6 +- ...ost_projectsprojectidvpcsvmsvmidreload.txt | 6 +- ...post_projectsprojectidvpcsvmsvmidstart.txt | 6 +- .../post_projectsprojectidvpcsvmsvmidstop.txt | 6 +- docs/api/examples/post_version.txt | 10 +- docs/api/examples/put_projectsprojectid.txt | 8 +- .../put_projectsprojectidiouvmsvmid.txt | 14 +-- .../put_projectsprojectidqemuvmsvmid.txt | 10 +- ...put_projectsprojectidvirtualboxvmsvmid.txt | 8 +- .../put_projectsprojectidvpcsvmsvmid.txt | 8 +- ...ojectsprojectiddynamipsdevicesdeviceid.rst | 6 +- ...mipsdevicesdeviceidportsportnumberdnio.rst | 8 +- ...esdeviceidportsportnumberdstartcapture.rst | 4 +- ...cesdeviceidportsportnumberdstopcapture.rst | 4 +- docs/api/v1/dynamips_vm/dynamipsvms.rst | 13 +++ .../projectsprojectiddynamipsvmsvmid.rst | 8 +- ...ptersadapternumberdportsportnumberdnio.rst | 8 +- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- ...projectsprojectiddynamipsvmsvmidreload.rst | 2 +- ...projectsprojectiddynamipsvmsvmidresume.rst | 2 +- .../projectsprojectiddynamipsvmsvmidstart.rst | 2 +- .../projectsprojectiddynamipsvmsvmidstop.rst | 2 +- ...rojectsprojectiddynamipsvmsvmidsuspend.rst | 2 +- docs/api/v1/file.rst | 8 ++ docs/api/v1/file/filesstream.rst | 24 +++++ docs/api/v1/iou/iouvms.rst | 13 +++ docs/api/v1/iou/projectsprojectidiouvms.rst | 3 +- .../v1/iou/projectsprojectidiouvmsvmid.rst | 8 +- ...ptersadapternumberdportsportnumberdnio.rst | 8 +- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- .../iou/projectsprojectidiouvmsvmidreload.rst | 2 +- .../iou/projectsprojectidiouvmsvmidstart.rst | 2 +- .../iou/projectsprojectidiouvmsvmidstop.rst | 2 +- docs/api/v1/qemu/projectsprojectidqemuvms.rst | 2 +- .../v1/qemu/projectsprojectidqemuvmsvmid.rst | 12 +-- ...ptersadapternumberdportsportnumberdnio.rst | 12 +-- .../projectsprojectidqemuvmsvmidreload.rst | 4 +- .../projectsprojectidqemuvmsvmidresume.rst | 4 +- .../projectsprojectidqemuvmsvmidstart.rst | 4 +- .../qemu/projectsprojectidqemuvmsvmidstop.rst | 4 +- .../projectsprojectidqemuvmsvmidsuspend.rst | 4 +- docs/api/v1/qemu/qemuvms.rst | 13 +++ .../projectsprojectidvirtualboxvmsvmid.rst | 6 +- ...ptersadapternumberdportsportnumberdnio.rst | 8 +- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- ...ojectsprojectidvirtualboxvmsvmidreload.rst | 2 +- ...ojectsprojectidvirtualboxvmsvmidresume.rst | 2 +- ...rojectsprojectidvirtualboxvmsvmidstart.rst | 2 +- ...projectsprojectidvirtualboxvmsvmidstop.rst | 2 +- ...jectsprojectidvirtualboxvmsvmidsuspend.rst | 2 +- docs/api/v1/vmware.rst | 8 ++ .../v1/vmware/projectsprojectidvmwarevms.rst | 49 ++++++++++ .../vmware/projectsprojectidvmwarevmsvmid.rst | 96 +++++++++++++++++++ .../projectsprojectidvmwarevmsvmidreload.rst | 20 ++++ .../projectsprojectidvmwarevmsvmidresume.rst | 20 ++++ .../projectsprojectidvmwarevmsvmidstart.rst | 20 ++++ .../projectsprojectidvmwarevmsvmidstop.rst | 20 ++++ .../projectsprojectidvmwarevmsvmidsuspend.rst | 20 ++++ .../v1/vpcs/projectsprojectidvpcsvmsvmid.rst | 6 +- ...ptersadapternumberdportsportnumberdnio.rst | 8 +- .../projectsprojectidvpcsvmsvmidreload.rst | 2 +- .../projectsprojectidvpcsvmsvmidstart.rst | 2 +- .../vpcs/projectsprojectidvpcsvmsvmidstop.rst | 2 +- tests/modules/iou/test_iou_vm.py | 4 +- 111 files changed, 605 insertions(+), 288 deletions(-) create mode 100644 docs/api/v1/dynamips_vm/dynamipsvms.rst create mode 100644 docs/api/v1/file.rst create mode 100644 docs/api/v1/file/filesstream.rst create mode 100644 docs/api/v1/iou/iouvms.rst create mode 100644 docs/api/v1/qemu/qemuvms.rst create mode 100644 docs/api/v1/vmware.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevms.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidreload.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidresume.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstart.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstop.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidsuspend.rst diff --git a/docs/api/examples/delete_projectsprojectid.txt b/docs/api/examples/delete_projectsprojectid.txt index a7ecd00f..9b15153a 100644 --- a/docs/api/examples/delete_projectsprojectid.txt +++ b/docs/api/examples/delete_projectsprojectid.txt @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id} diff --git a/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt b/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt index ae31ce71..d1fa8cf3 100644 --- a/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/becc8bf8-1936-4076-b7dd-ee83ba078907' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/32773db2-f466-4118-8bef-02b585f92619' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/becc8bf8-1936-4076-b7dd-ee83ba078907 HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/32773db2-f466-4118-8bef-02b585f92619 HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} diff --git a/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 94d35620..eb8f1a08 100644 --- a/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/779f3f81-60ab-4d26-860c-75f915b1d70b/adapters/1/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/22fb7e95-ac48-466a-a5e7-a03d2c9b57aa/adapters/1/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/779f3f81-60ab-4d26-860c-75f915b1d70b/adapters/1/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/22fb7e95-ac48-466a-a5e7-a03d2c9b57aa/adapters/1/ports/0/nio HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio diff --git a/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt index c8d13190..84a4fcde 100644 --- a/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/6537c17b-2e47-46f9-b96b-6fc66382709f' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/df85e698-3851-4172-b195-2caeb3fc5cf6' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/6537c17b-2e47-46f9-b96b-6fc66382709f HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/df85e698-3851-4172-b195-2caeb3fc5cf6 HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} diff --git a/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 3689a022..89e38b92 100644 --- a/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/b54e4616-058f-413d-a589-0b7c5da20aa3/adapters/1/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2a5cfc20-5a1f-4180-897a-c90362a01832/adapters/1/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/b54e4616-058f-413d-a589-0b7c5da20aa3/adapters/1/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2a5cfc20-5a1f-4180-897a-c90362a01832/adapters/1/ports/0/nio HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio diff --git a/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 75b27845..f67cc1d4 100644 --- a/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/387fb016-f1fc-4844-a25e-97c08ef77274/adapters/0/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c413ed57-0064-4e42-9cc0-59a6dc3143e0/adapters/0/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/387fb016-f1fc-4844-a25e-97c08ef77274/adapters/0/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c413ed57-0064-4e42-9cc0-59a6dc3143e0/adapters/0/ports/0/nio HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio diff --git a/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt index ec2dd646..b50c3a72 100644 --- a/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/6bc73868-fea6-449b-af5c-e7b746e4129d' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/44a0ec89-6cb1-4165-a4d5-ebf99963440f' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/6bc73868-fea6-449b-af5c-e7b746e4129d HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/44a0ec89-6cb1-4165-a4d5-ebf99963440f HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} diff --git a/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 8c8e8be2..7a27263a 100644 --- a/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f7d1d6ad-422b-486a-8d23-1ff46cd8bc4b/adapters/0/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f12f73cc-dfd6-4b04-a731-1e0e6ead343a/adapters/0/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f7d1d6ad-422b-486a-8d23-1ff46cd8bc4b/adapters/0/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f12f73cc-dfd6-4b04-a731-1e0e6ead343a/adapters/0/ports/0/nio HTTP/1.1 @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio diff --git a/docs/api/examples/get_interfaces.txt b/docs/api/examples/get_interfaces.txt index 780bc706..d332fbe2 100644 --- a/docs/api/examples/get_interfaces.txt +++ b/docs/api/examples/get_interfaces.txt @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 718 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/interfaces [ @@ -25,10 +25,6 @@ X-ROUTE: /v1/interfaces "id": "stf0", "name": "stf0" }, - { - "id": "en0", - "name": "en0" - }, { "id": "en1", "name": "en1" @@ -41,6 +37,10 @@ X-ROUTE: /v1/interfaces "id": "en2", "name": "en2" }, + { + "id": "en0", + "name": "en0" + }, { "id": "p2p0", "name": "p2p0" diff --git a/docs/api/examples/get_projectsprojectid.txt b/docs/api/examples/get_projectsprojectid.txt index ebe85d97..c2fd7629 100644 --- a/docs/api/examples/get_projectsprojectid.txt +++ b/docs/api/examples/get_projectsprojectid.txt @@ -9,13 +9,13 @@ CONNECTION: keep-alive CONTENT-LENGTH: 297 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id} { - "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmphbzo0jp9", + "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpqyrnrqv5", "name": "test", - "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmphbzo0jp9/00010203-0405-0607-0809-0a0b0c0d0e02", + "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpqyrnrqv5/00010203-0405-0607-0809-0a0b0c0d0e02", "project_id": "00010203-0405-0607-0809-0a0b0c0d0e02", "temporary": false } diff --git a/docs/api/examples/get_projectsprojectidiouvmsvmid.txt b/docs/api/examples/get_projectsprojectidiouvmsvmid.txt index 688fa3d7..169d6822 100644 --- a/docs/api/examples/get_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidiouvmsvmid.txt @@ -1,15 +1,15 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/ede1bba5-0723-4fd8-9e89-bdfffe5f5c8f' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/570b3403-25ed-44bb-ab47-cbead7965a46' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/ede1bba5-0723-4fd8-9e89-bdfffe5f5c8f HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/570b3403-25ed-44bb-ab47-cbead7965a46 HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 386 +CONTENT-LENGTH: 467 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} { @@ -20,10 +20,10 @@ X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} "l1_keepalives": false, "name": "PC TEST 1", "nvram": 128, - "path": "iou.bin", + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_get0/iou.bin", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 256, "serial_adapters": 2, "use_default_iou_values": true, - "vm_id": "ede1bba5-0723-4fd8-9e89-bdfffe5f5c8f" + "vm_id": "570b3403-25ed-44bb-ab47-cbead7965a46" } diff --git a/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt b/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt index 4b3abdb5..fc6ef2c5 100644 --- a/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt +++ b/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt @@ -1,6 +1,6 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/821b0dc3-4cc0-4899-8184-75bfc22db584/initial_config' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/733d8688-05fd-4e25-86fb-9ce302264936/initial_config' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/821b0dc3-4cc0-4899-8184-75bfc22db584/initial_config HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/733d8688-05fd-4e25-86fb-9ce302264936/initial_config HTTP/1.1 @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 25 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/initial_config { diff --git a/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt index 26de1035..0352bb8c 100644 --- a/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e3935d2d-2bf9-4cde-8c7e-0bd1d74c3dad' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/86cb38a5-d580-41b2-ab31-57dd5c2e2317' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e3935d2d-2bf9-4cde-8c7e-0bd1d74c3dad HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/86cb38a5-d580-41b2-ab31-57dd5c2e2317 HTTP/1.1 @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 597 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} { @@ -29,7 +29,7 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpyasp9636/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 256, - "vm_id": "e3935d2d-2bf9-4cde-8c7e-0bd1d74c3dad" + "vm_id": "86cb38a5-d580-41b2-ab31-57dd5c2e2317" } diff --git a/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt b/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt index cd98bab1..808686b4 100644 --- a/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f49ff215-0872-4bf7-90c8-3d9ecc2b2f2b' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c6006b62-b9c1-4be0-9cc6-81d12bdd29d5' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f49ff215-0872-4bf7-90c8-3d9ecc2b2f2b HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c6006b62-b9c1-4be0-9cc6-81d12bdd29d5 HTTP/1.1 @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 361 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} { @@ -22,6 +22,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "f49ff215-0872-4bf7-90c8-3d9ecc2b2f2b", + "vm_id": "c6006b62-b9c1-4be0-9cc6-81d12bdd29d5", "vmname": "VMTEST" } diff --git a/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt index 195eb61a..218c2d63 100644 --- a/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3ff6ff9e-93de-45ae-a7ec-e296d406ffe1' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3ff6ff9e-93de-45ae-a7ec-e296d406ffe1 HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e HTTP/1.1 @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 220 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} { @@ -18,5 +18,5 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": null, "startup_script_path": null, - "vm_id": "3ff6ff9e-93de-45ae-a7ec-e296d406ffe1" + "vm_id": "3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e" } diff --git a/docs/api/examples/get_qemubinaries.txt b/docs/api/examples/get_qemubinaries.txt index 003055b4..16ecb792 100644 --- a/docs/api/examples/get_qemubinaries.txt +++ b/docs/api/examples/get_qemubinaries.txt @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 134 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/qemu/binaries [ diff --git a/docs/api/examples/get_version.txt b/docs/api/examples/get_version.txt index d267bb31..f190f8b8 100644 --- a/docs/api/examples/get_version.txt +++ b/docs/api/examples/get_version.txt @@ -6,13 +6,13 @@ GET /v1/version HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 49 +CONTENT-LENGTH: 50 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/version { "local": true, - "version": "1.3.1.rc2" + "version": "1.4.0.dev1" } diff --git a/docs/api/examples/post_configreload.txt b/docs/api/examples/post_configreload.txt index 35f71f23..c6bead3d 100644 --- a/docs/api/examples/post_configreload.txt +++ b/docs/api/examples/post_configreload.txt @@ -8,6 +8,6 @@ HTTP/1.1 201 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/config/reload diff --git a/docs/api/examples/post_projects.txt b/docs/api/examples/post_projects.txt index c53e1fd7..83c6f912 100644 --- a/docs/api/examples/post_projects.txt +++ b/docs/api/examples/post_projects.txt @@ -11,13 +11,13 @@ CONNECTION: keep-alive CONTENT-LENGTH: 297 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects { - "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp_6lclsv7", + "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpue5qtaa2", "name": "test", - "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp_6lclsv7/187543e2-5d46-4108-a623-cfdd31fa300e", - "project_id": "187543e2-5d46-4108-a623-cfdd31fa300e", + "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpue5qtaa2/abc875ab-6e67-484c-8db0-4e05de5f00b0", + "project_id": "abc875ab-6e67-484c-8db0-4e05de5f00b0", "temporary": false } diff --git a/docs/api/examples/post_projectsprojectidclose.txt b/docs/api/examples/post_projectsprojectidclose.txt index a73b52d5..f0f7fa66 100644 --- a/docs/api/examples/post_projectsprojectidclose.txt +++ b/docs/api/examples/post_projectsprojectidclose.txt @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/close diff --git a/docs/api/examples/post_projectsprojectidcommit.txt b/docs/api/examples/post_projectsprojectidcommit.txt index 1bc0885f..e76c4d40 100644 --- a/docs/api/examples/post_projectsprojectidcommit.txt +++ b/docs/api/examples/post_projectsprojectidcommit.txt @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/commit diff --git a/docs/api/examples/post_projectsprojectidiouvms.txt b/docs/api/examples/post_projectsprojectidiouvms.txt index f7af542c..252cd4c3 100644 --- a/docs/api/examples/post_projectsprojectidiouvms.txt +++ b/docs/api/examples/post_projectsprojectidiouvms.txt @@ -1,40 +1,34 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms' -d '{"ethernet_adapters": 0, "initial_config_content": "hostname test", "iourc_content": "test", "l1_keepalives": true, "name": "PC TEST 1", "nvram": 512, "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-4450/test_iou_create_with_params0/iou.bin", "ram": 1024, "serial_adapters": 4, "use_default_iou_values": true}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms' -d '{"initial_config_content": "hostname test", "name": "PC TEST 1", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899"}' POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms HTTP/1.1 { - "ethernet_adapters": 0, "initial_config_content": "hostname test", - "iourc_content": "test", - "l1_keepalives": true, "name": "PC TEST 1", - "nvram": 512, - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-4450/test_iou_create_with_params0/iou.bin", - "ram": 1024, - "serial_adapters": 4, - "use_default_iou_values": true + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", + "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899" } HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 466 +CONTENT-LENGTH: 501 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms { "console": 2000, - "ethernet_adapters": 0, + "ethernet_adapters": 2, "initial_config": "initial-config.cfg", - "iourc_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmps796q8lx/iourc", - "l1_keepalives": true, + "iourc_path": null, + "l1_keepalives": false, "name": "PC TEST 1", - "nvram": 512, - "path": "iou.bin", + "nvram": 128, + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "ram": 1024, - "serial_adapters": 4, + "ram": 256, + "serial_adapters": 2, "use_default_iou_values": true, - "vm_id": "69f5842a-d4e5-45fe-a500-1010c72f1748" + "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899" } diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 97dc6046..c66434a6 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/04648c72-4339-471e-aa12-57b42d7ea18b/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/5fff6292-2ac7-4f25-9fa7-5bdaaa7fbe84/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/04648c72-4339-471e-aa12-57b42d7ea18b/adapters/1/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/5fff6292-2ac7-4f25-9fa7-5bdaaa7fbe84/adapters/1/ports/0/nio HTTP/1.1 { "ethernet_device": "eth0", "type": "nio_generic_ethernet" @@ -12,7 +12,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 69 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio { diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt index b91d6987..1f1ad3bf 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/6056b617-d0c4-4683-bc9e-d0130beec951/adapters/0/ports/0/start_capture' -d '{"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3101e74a-8456-4ccf-ba7a-fd1b3cdf0729/adapters/0/ports/0/start_capture' -d '{"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/6056b617-d0c4-4683-bc9e-d0130beec951/adapters/0/ports/0/start_capture HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3101e74a-8456-4ccf-ba7a-fd1b3cdf0729/adapters/0/ports/0/start_capture HTTP/1.1 { "capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB" @@ -12,9 +12,9 @@ CONNECTION: keep-alive CONTENT-LENGTH: 158 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture { - "pcap_file_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp367810hd/a1e920ca-338a-4e9f-b363-aa607b09dd80/project-files/captures/test.pcap" + "pcap_file_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpossrvwje/a1e920ca-338a-4e9f-b363-aa607b09dd80/project-files/captures/test.pcap" } diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt index 519c2f2c..0c85536f 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0fb3c393-7cda-4abc-ad76-a9e7af04abea/adapters/0/ports/0/stop_capture' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2309d25b-8095-4a1a-9478-6345dad9f579/adapters/0/ports/0/stop_capture' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0fb3c393-7cda-4abc-ad76-a9e7af04abea/adapters/0/ports/0/stop_capture HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2309d25b-8095-4a1a-9478-6345dad9f579/adapters/0/ports/0/stop_capture HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt index 8bce1041..67c1ec57 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b32c38db-3692-4719-b94f-2b3f664cd06f/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/c4018290-fe62-41c8-bf06-88d832eb0477/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b32c38db-3692-4719-b94f-2b3f664cd06f/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/c4018290-fe62-41c8-bf06-88d832eb0477/reload HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/reload diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt index 5f5e6079..6666f708 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/55209a6f-8fe0-49d1-a884-b7cd09547b5c/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2fe292e5-ab46-473a-bd91-edf0ea58fc47/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/55209a6f-8fe0-49d1-a884-b7cd09547b5c/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2fe292e5-ab46-473a-bd91-edf0ea58fc47/start HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/start diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt index 66a5ebee..bee9429d 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/d893bd2c-d84e-4f89-ad84-f3bdfaf460b6/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/8dc543e0-ae2c-45cc-bdc2-82baa781c8a7/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/d893bd2c-d84e-4f89-ad84-f3bdfaf460b6/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/8dc543e0-ae2c-45cc-bdc2-82baa781c8a7/stop HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/stop diff --git a/docs/api/examples/post_projectsprojectidportsudp.txt b/docs/api/examples/post_projectsprojectidportsudp.txt index 6ee01042..7f5ee580 100644 --- a/docs/api/examples/post_projectsprojectidportsudp.txt +++ b/docs/api/examples/post_projectsprojectidportsudp.txt @@ -9,7 +9,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 25 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/ports/udp { diff --git a/docs/api/examples/post_projectsprojectidqemuvms.txt b/docs/api/examples/post_projectsprojectidqemuvms.txt index 7a780fa4..74beb465 100644 --- a/docs/api/examples/post_projectsprojectidqemuvms.txt +++ b/docs/api/examples/post_projectsprojectidqemuvms.txt @@ -1,10 +1,10 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms' -d '{"hda_disk_image": "/tmp/hda", "name": "PC TEST 1", "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpyasp9636/qemu_x42", "ram": 1024}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms' -d '{"hda_disk_image": "/tmp/hda", "name": "PC TEST 1", "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 1024}' POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms HTTP/1.1 { "hda_disk_image": "/tmp/hda", "name": "PC TEST 1", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpyasp9636/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 1024 } @@ -14,7 +14,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 606 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms { @@ -34,7 +34,7 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpyasp9636/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 1024, - "vm_id": "8c325041-39a8-4c31-b921-b66dadadc353" + "vm_id": "c0fd0a60-1952-47e7-a293-7a233c83ce8e" } diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 59d13e6a..1797d515 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,21 +1,21 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e791569e-7f95-4a1d-9f8d-b48611afeef3/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d378a8bd-711d-47b1-aaa0-dfa0c3ff420b/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e791569e-7f95-4a1d-9f8d-b48611afeef3/adapters/1/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d378a8bd-711d-47b1-aaa0-dfa0c3ff420b/adapters/1/ports/0/nio HTTP/1.1 { "ethernet_device": "eth0", "type": "nio_generic_ethernet" } -HTTP/1.1 201 +HTTP/1.1 409 CONNECTION: keep-alive -CONTENT-LENGTH: 69 +CONTENT-LENGTH: 89 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio { - "ethernet_device": "eth0", - "type": "nio_generic_ethernet" + "message": "NIO of type nio_generic_ethernet is not supported", + "status": 409 } diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt index 5c2a2bf8..a80380b1 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e6de45b1-048b-498b-9875-de76762532e9/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/96e58ba8-0d90-4f45-a410-ba738338b5f8/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e6de45b1-048b-498b-9875-de76762532e9/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/96e58ba8-0d90-4f45-a410-ba738338b5f8/reload HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/reload diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt index 85ba0910..ebd6e3c9 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d7152d7c-7f23-4d92-9ee1-fae132a50b3b/resume' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/f112cd1e-d307-4f95-ab6b-8d2387d520d5/resume' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d7152d7c-7f23-4d92-9ee1-fae132a50b3b/resume HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/f112cd1e-d307-4f95-ab6b-8d2387d520d5/resume HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/resume diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt index 0cd46103..d484e275 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/031c1e57-577c-4ff4-91d1-da6fe0816fdd/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/14c7e896-28cf-4705-801c-76ab2a4ee387/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/031c1e57-577c-4ff4-91d1-da6fe0816fdd/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/14c7e896-28cf-4705-801c-76ab2a4ee387/start HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/start diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt index 31095727..9cb889e1 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/79ca17e8-0194-4682-bb2a-0bdd9f7d1e1a/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d730f891-a552-4df5-9399-fab1ed15358e/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/79ca17e8-0194-4682-bb2a-0bdd9f7d1e1a/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d730f891-a552-4df5-9399-fab1ed15358e/stop HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/stop diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt index 2b8146c9..b45534fc 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/7e24f49b-51ea-410f-bc94-16fc58071493/suspend' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/64c8e5e5-056b-4ec8-9d86-912c7e053560/suspend' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/7e24f49b-51ea-410f-bc94-16fc58071493/suspend HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/64c8e5e5-056b-4ec8-9d86-912c7e053560/suspend HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id}/suspend diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvms.txt b/docs/api/examples/post_projectsprojectidvirtualboxvms.txt index bae91853..1cd38b1d 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvms.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvms.txt @@ -13,7 +13,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 355 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms { @@ -26,6 +26,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "fd208626-81e5-449e-b145-fd6993f5097c", + "vm_id": "f9407c6b-39bf-4445-9d53-70676fe7e3dc", "vmname": "VM1" } diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt index cf095b1b..fa91f290 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/5463a797-0eb0-41d2-8b32-1efbd7a353cc/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/d18556e2-fc68-486c-a039-0cd9200322d3/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/5463a797-0eb0-41d2-8b32-1efbd7a353cc/adapters/0/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/d18556e2-fc68-486c-a039-0cd9200322d3/adapters/0/ports/0/nio HTTP/1.1 { "lport": 4242, "rhost": "127.0.0.1", @@ -14,7 +14,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 89 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio { diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt index 54f0322a..4004050e 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f24209c8-671e-428a-9561-db4775f6b8a7/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/cb66c1db-e2f8-4149-9065-976db1af7afb/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f24209c8-671e-428a-9561-db4775f6b8a7/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/cb66c1db-e2f8-4149-9065-976db1af7afb/reload HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/reload diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt index 039ec17f..cc304ddd 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/70df82f6-e868-4ab3-9be9-d456871f41dc/resume' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/158053af-713e-4a72-a05d-c33aa05208f7/resume' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/70df82f6-e868-4ab3-9be9-d456871f41dc/resume HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/158053af-713e-4a72-a05d-c33aa05208f7/resume HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/resume diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt index 1f3ad83e..34ae9b83 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/354d48fe-cab6-41d8-8cc1-64716e02c3a8/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/a18cc3a2-a31b-4d4a-b79a-0b1e714760fb/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/354d48fe-cab6-41d8-8cc1-64716e02c3a8/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/a18cc3a2-a31b-4d4a-b79a-0b1e714760fb/start HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/start diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt index 9c16d5c2..41397c9e 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/90733316-a02a-490b-b8c4-e6ea4a32296a/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b897ee5-9c9f-4de1-9858-2a0280d2e551/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/90733316-a02a-490b-b8c4-e6ea4a32296a/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b897ee5-9c9f-4de1-9858-2a0280d2e551/stop HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/stop diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt index 6cd4c9bc..f9cab5fe 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/0f257aa5-d61d-4ee9-872a-898462f7ecdf/suspend' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b5139c6-3bcd-4edc-a37f-fd73d28cd9bd/suspend' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/0f257aa5-d61d-4ee9-872a-898462f7ecdf/suspend HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b5139c6-3bcd-4edc-a37f-fd73d28cd9bd/suspend HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id}/suspend diff --git a/docs/api/examples/post_projectsprojectidvpcsvms.txt b/docs/api/examples/post_projectsprojectidvpcsvms.txt index 629a420f..5d9f94ad 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvms.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvms.txt @@ -11,7 +11,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 220 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms { @@ -20,5 +20,5 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": null, "startup_script_path": null, - "vm_id": "68c6af80-0a82-406e-b051-24c95bd728f4" + "vm_id": "104b72e1-1d73-4153-989f-96731442f6a1" } diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 360ffc76..fa73145e 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4125e37f-2bf1-435c-a86c-ae1fce4c916a/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/b5d32250-bacf-4594-be35-e2d2d814a45c/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4125e37f-2bf1-435c-a86c-ae1fce4c916a/adapters/0/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/b5d32250-bacf-4594-be35-e2d2d814a45c/adapters/0/ports/0/nio HTTP/1.1 { "lport": 4242, "rhost": "127.0.0.1", @@ -14,7 +14,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 89 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio { diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt index 7aa26e08..edc0e24e 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4c5c5174-07bf-4f2a-93ab-cec244e24852/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/722b7e9d-ecc1-44cf-b743-609bb267ec65/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4c5c5174-07bf-4f2a-93ab-cec244e24852/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/722b7e9d-ecc1-44cf-b743-609bb267ec65/reload HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id}/reload diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt index 04eae9b0..ba660775 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/43ac3ea8-78a7-405e-ad5e-653293c48e66/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/0986e25f-6d7e-4a64-a8ea-b1a3a5541c46/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/43ac3ea8-78a7-405e-ad5e-653293c48e66/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/0986e25f-6d7e-4a64-a8ea-b1a3a5541c46/start HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id}/start diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt index a8a44b4f..2271f66c 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/59df4b41-6a23-4c99-9370-600b3a2cff23/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/a9b859ce-d847-402c-9db2-cd2d1bc4f67a/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/59df4b41-6a23-4c99-9370-600b3a2cff23/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/a9b859ce-d847-402c-9db2-cd2d1bc4f67a/stop HTTP/1.1 {} @@ -8,6 +8,6 @@ HTTP/1.1 204 CONNECTION: keep-alive CONTENT-LENGTH: 0 DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id}/stop diff --git a/docs/api/examples/post_version.txt b/docs/api/examples/post_version.txt index 1bd3ea12..b26a6066 100644 --- a/docs/api/examples/post_version.txt +++ b/docs/api/examples/post_version.txt @@ -1,19 +1,19 @@ -curl -i -X POST 'http://localhost:8000/v1/version' -d '{"version": "1.3.1.rc2"}' +curl -i -X POST 'http://localhost:8000/v1/version' -d '{"version": "1.4.0.dev1"}' POST /v1/version HTTP/1.1 { - "version": "1.3.1.rc2" + "version": "1.4.0.dev1" } HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 30 +CONTENT-LENGTH: 31 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/version { - "version": "1.3.1.rc2" + "version": "1.4.0.dev1" } diff --git a/docs/api/examples/put_projectsprojectid.txt b/docs/api/examples/put_projectsprojectid.txt index 54cf8429..13f3f6a7 100644 --- a/docs/api/examples/put_projectsprojectid.txt +++ b/docs/api/examples/put_projectsprojectid.txt @@ -1,9 +1,9 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/b32aab45-411c-4171-9f20-357eaa00d54c' -d '{"name": "second_name", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-4450/test_update_path_project_non_l0"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/ac4b48cf-f26c-4ab6-bce1-368fdffb112f' -d '{"name": "second_name", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_update_path_project_non_l0"}' -PUT /v1/projects/b32aab45-411c-4171-9f20-357eaa00d54c HTTP/1.1 +PUT /v1/projects/ac4b48cf-f26c-4ab6-bce1-368fdffb112f HTTP/1.1 { "name": "second_name", - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-4450/test_update_path_project_non_l0" + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_update_path_project_non_l0" } @@ -12,7 +12,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 100 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id} { diff --git a/docs/api/examples/put_projectsprojectidiouvmsvmid.txt b/docs/api/examples/put_projectsprojectidiouvmsvmid.txt index 80cd3523..fc1bc621 100644 --- a/docs/api/examples/put_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidiouvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3812eced-e777-4f35-8c7e-e6736e34fcfd' -d '{"console": 2001, "ethernet_adapters": 4, "initial_config_content": "hostname test", "iourc_content": "test", "l1_keepalives": true, "name": "test", "nvram": 2048, "ram": 512, "serial_adapters": 0, "use_default_iou_values": true}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b75e3ec5-b42d-4e42-80a3-e79b2aab1efa' -d '{"console": 2001, "ethernet_adapters": 4, "initial_config_content": "hostname test", "iourc_content": "test", "l1_keepalives": true, "name": "test", "nvram": 2048, "ram": 512, "serial_adapters": 0, "use_default_iou_values": true}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3812eced-e777-4f35-8c7e-e6736e34fcfd HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b75e3ec5-b42d-4e42-80a3-e79b2aab1efa HTTP/1.1 { "console": 2001, "ethernet_adapters": 4, @@ -17,24 +17,24 @@ PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3812eced-e777-4f35 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 461 +CONTENT-LENGTH: 545 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} { "console": 2001, "ethernet_adapters": 4, "initial_config": "initial-config.cfg", - "iourc_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpws7fdl5e/iourc", + "iourc_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmprshqaaaj/iourc", "l1_keepalives": true, "name": "test", "nvram": 2048, - "path": "iou.bin", + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_update0/iou.bin", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 512, "serial_adapters": 0, "use_default_iou_values": true, - "vm_id": "3812eced-e777-4f35-8c7e-e6736e34fcfd" + "vm_id": "b75e3ec5-b42d-4e42-80a3-e79b2aab1efa" } diff --git a/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt index 288411b1..43bfd71f 100644 --- a/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e5b90e65-6f6a-4d44-b2d8-3a5c3a4a626b' -d '{"console": 2001, "hdb_disk_image": "/tmp/hdb", "name": "test", "ram": 1024}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8338f125-5b24-4bc9-a891-6ca1510396ad' -d '{"console": 2001, "hdb_disk_image": "/tmp/hdb", "name": "test", "ram": 1024}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/e5b90e65-6f6a-4d44-b2d8-3a5c3a4a626b HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8338f125-5b24-4bc9-a891-6ca1510396ad HTTP/1.1 { "console": 2001, "hdb_disk_image": "/tmp/hdb", @@ -14,7 +14,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 601 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} { @@ -34,7 +34,7 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpyasp9636/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 1024, - "vm_id": "e5b90e65-6f6a-4d44-b2d8-3a5c3a4a626b" + "vm_id": "8338f125-5b24-4bc9-a891-6ca1510396ad" } diff --git a/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt b/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt index 68ec4c22..b4070901 100644 --- a/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/514963f5-93f8-4d18-bcd6-7d50ef7164a6' -d '{"console": 2010, "name": "test"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/4dac8148-a8d2-42a5-9f91-805c3490e0f6' -d '{"console": 2010, "name": "test"}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/514963f5-93f8-4d18-bcd6-7d50ef7164a6 HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/4dac8148-a8d2-42a5-9f91-805c3490e0f6 HTTP/1.1 { "console": 2010, "name": "test" @@ -12,7 +12,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 359 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} { @@ -25,6 +25,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "514963f5-93f8-4d18-bcd6-7d50ef7164a6", + "vm_id": "4dac8148-a8d2-42a5-9f91-805c3490e0f6", "vmname": "VMTEST" } diff --git a/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt index 70c8462c..f0007143 100644 --- a/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4905e649-6e81-446d-a60b-fd8b058a85e8' -d '{"console": 2011, "name": "test", "startup_script": "ip 192.168.1.1"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/19169b30-8673-43e6-a096-11a6ebea1538' -d '{"console": 2011, "name": "test", "startup_script": "ip 192.168.1.1"}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/4905e649-6e81-446d-a60b-fd8b058a85e8 HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/19169b30-8673-43e6-a096-11a6ebea1538 HTTP/1.1 { "console": 2011, "name": "test", @@ -13,7 +13,7 @@ CONNECTION: keep-alive CONTENT-LENGTH: 236 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.3.1.rc2 +SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} { @@ -22,5 +22,5 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": "ip 192.168.1.1", "startup_script_path": "startup.vpc", - "vm_id": "4905e649-6e81-446d-a60b-fd8b058a85e8" + "vm_id": "19169b30-8673-43e6-a096-11a6ebea1538" } diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst index 7f0e65e4..1ff726ba 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst @@ -9,8 +9,8 @@ Get a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project - **device_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -38,8 +38,8 @@ Update a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project - **device_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -95,8 +95,8 @@ Delete a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project - **device_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst index 0533ad08..f74e9656 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst @@ -9,9 +9,9 @@ Add a NIO to a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project -- **port_number**: Port on the device - **device_id**: UUID for the instance +- **port_number**: Port on the device +- **project_id**: UUID for the project Response status codes ********************** @@ -128,9 +128,9 @@ Remove a NIO from a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project -- **port_number**: Port on the device - **device_id**: UUID for the instance +- **port_number**: Port on the device +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst index 117cd928..a33ba970 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst @@ -9,9 +9,9 @@ Start a packet capture on a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project -- **port_number**: Port on the device - **device_id**: UUID for the instance +- **port_number**: Port on the device +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst index 9674ef65..75cf96a4 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst @@ -9,9 +9,9 @@ Stop a packet capture on a Dynamips device instance Parameters ********** -- **project_id**: UUID for the project -- **port_number**: Port on the device - **device_id**: UUID for the instance +- **port_number**: Port on the device +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/dynamipsvms.rst b/docs/api/v1/dynamips_vm/dynamipsvms.rst new file mode 100644 index 00000000..476814cd --- /dev/null +++ b/docs/api/v1/dynamips_vm/dynamipsvms.rst @@ -0,0 +1,13 @@ +/v1/dynamips/vms +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/dynamips/vms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Retrieve the list of Dynamips VMS + +Response status codes +********************** +- **200**: List of Dynamips VM retrieved + diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst index 0ec4f747..9f8b27db 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst @@ -9,8 +9,8 @@ Get a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -74,8 +74,8 @@ Update a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -110,7 +110,6 @@ Input nvram integer amount of NVRAM in KB platform string platform power_supplies array Power supplies status - private_config string path to the IOS private configuration file private_config_base64 string private configuration base64 encoded private_config_content string Content of IOS private configuration file ram integer amount of RAM in MB @@ -123,7 +122,6 @@ Input slot5 Network module slot 5 slot6 Network module slot 6 sparsemem boolean sparse memory feature - startup_config string path to the IOS startup configuration file startup_config_base64 string startup configuration base64 encoded startup_config_content string Content of IOS startup configuration file system_id string system ID @@ -188,8 +186,8 @@ Delete a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst index c1c114ec..7886ad07 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter where the nio should be added - **port_number**: Port on the adapter - **vm_id**: UUID for the instance +- **adapter_number**: Adapter where the nio should be added +- **project_id**: UUID for the project Response status codes ********************** @@ -27,10 +27,10 @@ Remove a NIO from a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter from where the nio should be removed - **port_number**: Port on the adapter - **vm_id**: UUID for the instance +- **adapter_number**: Adapter from where the nio should be removed +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index 57271aac..6cc4d452 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to start a packet capture - **port_number**: Port on the adapter - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to start a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index c3b44232..ce104421 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to stop a packet capture - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to stop a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidreload.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidreload.rst index 23bb67f3..d7d7df55 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidreload.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidreload.rst @@ -9,8 +9,8 @@ Reload a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidresume.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidresume.rst index 73ce9d01..fcd48ab5 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidresume.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidresume.rst @@ -9,8 +9,8 @@ Resume a suspended Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstart.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstart.rst index 24c3d2af..2dbd25b8 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstart.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstart.rst @@ -9,8 +9,8 @@ Start a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstop.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstop.rst index fca471b6..ff62c2c9 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstop.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidstop.rst @@ -9,8 +9,8 @@ Stop a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidsuspend.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidsuspend.rst index 54394e01..b6fb8a13 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidsuspend.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidsuspend.rst @@ -9,8 +9,8 @@ Suspend a Dynamips VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/file.rst b/docs/api/v1/file.rst new file mode 100644 index 00000000..47b74965 --- /dev/null +++ b/docs/api/v1/file.rst @@ -0,0 +1,8 @@ +File +--------------------- + +.. toctree:: + :glob: + :maxdepth: 2 + + file/* diff --git a/docs/api/v1/file/filesstream.rst b/docs/api/v1/file/filesstream.rst new file mode 100644 index 00000000..7d61f7ce --- /dev/null +++ b/docs/api/v1/file/filesstream.rst @@ -0,0 +1,24 @@ +/v1/files/stream +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/files/stream +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Stream a file from the server + +Response status codes +********************** +- **200**: File retrieved +- **409**: Can't access to file +- **404**: File doesn't exist + +Input +******* +.. raw:: html + + + + +
Name Mandatory Type Description
location ['string'] File path
+ diff --git a/docs/api/v1/iou/iouvms.rst b/docs/api/v1/iou/iouvms.rst new file mode 100644 index 00000000..80be4a0c --- /dev/null +++ b/docs/api/v1/iou/iouvms.rst @@ -0,0 +1,13 @@ +/v1/iou/vms +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/iou/vms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Retrieve the list of IOU VMS + +Response status codes +********************** +- **200**: List of IOU VM retrieved + diff --git a/docs/api/v1/iou/projectsprojectidiouvms.rst b/docs/api/v1/iou/projectsprojectidiouvms.rst index 6387045e..e780349e 100644 --- a/docs/api/v1/iou/projectsprojectidiouvms.rst +++ b/docs/api/v1/iou/projectsprojectidiouvms.rst @@ -25,7 +25,8 @@ Input Name Mandatory Type Description console ['integer', 'null'] console TCP port ethernet_adapters integer How many ethernet adapters are connected to the IOU - initial_config_content ['string', 'null'] Initial configuration of the IOU + initial_config ['string', 'null'] Path to the initial configuration of IOU + initial_config_content ['string', 'null'] Initial configuration of IOU iourc_content ['string', 'null'] Content of the iourc file, if a file exist on servers this variable is ignored. It's mostly for compatibility with < 1.3 releases l1_keepalives ['boolean', 'null'] Always up ethernet interface name ✔ string IOU VM name diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst index 87bba3cf..6d98a4bc 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst @@ -9,8 +9,8 @@ Get a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -52,8 +52,8 @@ Update a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -70,7 +70,7 @@ Input Name Mandatory Type Description console ['integer', 'null'] console TCP port ethernet_adapters ['integer', 'null'] How many ethernet adapters are connected to the IOU - initial_config_content ['string', 'null'] Initial configuration of the IOU + initial_config_content ['string', 'null'] Initial configuration of IOU iourc_content ['string', 'null'] Content of the iourc file, if a file exist on servers this variable is ignored. It's mostly for compatibility with < 1.3 releases l1_keepalives ['boolean', 'null'] Always up ethernet interface name ['string', 'null'] IOU VM name @@ -115,8 +115,8 @@ Delete a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 748ed1e2..4f203c58 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a IOU instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port where the nio should be added - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a IOU instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port from where the nio should be removed - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index cc7a521d..8886e031 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a IOU VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to start a packet capture - **port_number**: Port on the adapter - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to start a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index fb66ba89..a1b1d625 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a IOU VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to stop a packet capture - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to stop a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidreload.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidreload.rst index 49be3d31..b8d8bb1d 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidreload.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidreload.rst @@ -9,8 +9,8 @@ Reload a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidstart.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidstart.rst index 25a9e236..016018ff 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidstart.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidstart.rst @@ -9,8 +9,8 @@ Start a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidstop.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidstop.rst index 220c81ad..e5aed265 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidstop.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidstop.rst @@ -9,8 +9,8 @@ Stop a IOU instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvms.rst b/docs/api/v1/qemu/projectsprojectidqemuvms.rst index 6c3d8e65..62ce6757 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvms.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvms.rst @@ -5,7 +5,7 @@ POST /v1/projects/**{project_id}**/qemu/vms ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a new Qemu.instance +Create a new Qemu VM instance Parameters ********** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst index 396d5db1..7cf89de6 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst @@ -5,12 +5,12 @@ GET /v1/projects/**{project_id}**/qemu/vms/**{vm_id}** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Get a Qemu.instance +Get a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -54,12 +54,12 @@ Sample session PUT /v1/projects/**{project_id}**/qemu/vms/**{vm_id}** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Update a Qemu.instance +Update a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -129,12 +129,12 @@ Sample session DELETE /v1/projects/**{project_id}**/qemu/vms/**{vm_id}** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Delete a Qemu.instance +Delete a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst index d9a76999..71edd895 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -5,14 +5,14 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/adapters/**{adapter_number:\d+}**/ports/**{port_number:\d+}**/nio ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add a NIO to a Qemu.instance +Add a NIO to a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** @@ -29,14 +29,14 @@ Sample session DELETE /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/adapters/**{adapter_number:\d+}**/ports/**{port_number:\d+}**/nio ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Remove a NIO from a Qemu.instance +Remove a NIO from a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidreload.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidreload.rst index 5b29cec3..e6d5207a 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidreload.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidreload.rst @@ -5,12 +5,12 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/reload ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Reload a Qemu.instance +Reload a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidresume.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidresume.rst index 59b5c1f7..18438d24 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidresume.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidresume.rst @@ -5,12 +5,12 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/resume ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Resume a Qemu.instance +Resume a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstart.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstart.rst index f5306892..a6d8b5b8 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstart.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstart.rst @@ -5,12 +5,12 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/start ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Start a Qemu.instance +Start a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstop.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstop.rst index d5c41c96..6c3a7417 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstop.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidstop.rst @@ -5,12 +5,12 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/stop ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Stop a Qemu.instance +Stop a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidsuspend.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidsuspend.rst index 63e8c94a..eba15bfc 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidsuspend.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidsuspend.rst @@ -5,12 +5,12 @@ POST /v1/projects/**{project_id}**/qemu/vms/**{vm_id}**/suspend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Suspend a Qemu.instance +Suspend a Qemu VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/qemu/qemuvms.rst b/docs/api/v1/qemu/qemuvms.rst new file mode 100644 index 00000000..fa8208c9 --- /dev/null +++ b/docs/api/v1/qemu/qemuvms.rst @@ -0,0 +1,13 @@ +/v1/qemu/vms +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/qemu/vms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Retrieve the list of Qemu images + +Response status codes +********************** +- **200**: List of Qemu images retrieved + diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst index 6c974e12..d345c454 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst @@ -9,8 +9,8 @@ Get a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -50,8 +50,8 @@ Update a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -109,8 +109,8 @@ Delete a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 19b76253..c6e4393b 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter where the nio should be added - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter where the nio should be added +- **project_id**: UUID for the project Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter from where the nio should be removed - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter from where the nio should be removed +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index 47b4fdca..ac55cd81 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to start a packet capture - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to start a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index 057170b0..65899c26 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Adapter to stop a packet capture - **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance +- **adapter_number**: Adapter to stop a packet capture +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidreload.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidreload.rst index 9ae84c29..d3c83c0e 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidreload.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidreload.rst @@ -9,8 +9,8 @@ Reload a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidresume.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidresume.rst index 0fb9d427..e05c62dc 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidresume.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidresume.rst @@ -9,8 +9,8 @@ Resume a suspended VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstart.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstart.rst index 5e6a6c42..5901fdbf 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstart.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstart.rst @@ -9,8 +9,8 @@ Start a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstop.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstop.rst index 1eaac889..cc20ef18 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstop.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidstop.rst @@ -9,8 +9,8 @@ Stop a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidsuspend.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidsuspend.rst index ad7f469b..e957b666 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidsuspend.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidsuspend.rst @@ -9,8 +9,8 @@ Suspend a VirtualBox VM instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/vmware.rst b/docs/api/v1/vmware.rst new file mode 100644 index 00000000..6a3cf3a0 --- /dev/null +++ b/docs/api/v1/vmware.rst @@ -0,0 +1,8 @@ +Vmware +--------------------- + +.. toctree:: + :glob: + :maxdepth: 2 + + vmware/* diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevms.rst b/docs/api/v1/vmware/projectsprojectidvmwarevms.rst new file mode 100644 index 00000000..bb8ea8a1 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevms.rst @@ -0,0 +1,49 @@ +/v1/projects/{project_id}/vmware/vms +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Create a new VMware VM instance + +Parameters +********** +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **201**: Instance created +- **409**: Conflict + +Input +******* +.. raw:: html + + + + + + + + + + +
Name Mandatory Type Description
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
linked_clone boolean either the VM is a linked clone or not
name string VMware VM instance name
vm_id string VMware VM instance identifier
vmx_path string path to the vmx file
+ +Output +******* +.. raw:: html + + + + + + + + + + +
Name Mandatory Type Description
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
+ diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst new file mode 100644 index 00000000..54445e72 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst @@ -0,0 +1,96 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id} +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/projects/**{project_id}**/vmware/vms/**{vm_id}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Get a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **200**: Success +- **400**: Invalid request +- **404**: Instance doesn't exist + +Output +******* +.. raw:: html + + + + + + + + + + +
Name Mandatory Type Description
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
+ + +PUT /v1/projects/**{project_id}**/vmware/vms/**{vm_id}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Update a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **200**: Instance updated +- **400**: Invalid request +- **404**: Instance doesn't exist +- **409**: Conflict + +Input +******* +.. raw:: html + + + + + + + + +
Name Mandatory Type Description
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
vmx_path string path to the vmx file
+ +Output +******* +.. raw:: html + + + + + + + + + + +
Name Mandatory Type Description
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
+ + +DELETE /v1/projects/**{project_id}**/vmware/vms/**{vm_id}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Delete a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance deleted + diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidreload.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidreload.rst new file mode 100644 index 00000000..48cc00cc --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidreload.rst @@ -0,0 +1,20 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/reload +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/reload +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Reload a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance reloaded + diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidresume.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidresume.rst new file mode 100644 index 00000000..f8a72a32 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidresume.rst @@ -0,0 +1,20 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/resume +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/resume +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Resume a suspended VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance resumed + diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstart.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstart.rst new file mode 100644 index 00000000..b15387b7 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstart.rst @@ -0,0 +1,20 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/start +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/start +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Start a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance started + diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstop.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstop.rst new file mode 100644 index 00000000..6cc98d74 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidstop.rst @@ -0,0 +1,20 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/stop +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/stop +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Stop a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance stopped + diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidsuspend.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidsuspend.rst new file mode 100644 index 00000000..d1e80e27 --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidsuspend.rst @@ -0,0 +1,20 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/suspend +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/suspend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Suspend a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: Instance suspended + diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst index 08762d16..0ff49d0a 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst @@ -9,8 +9,8 @@ Get a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -45,8 +45,8 @@ Update a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** @@ -93,8 +93,8 @@ Delete a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 7592c97c..21a4ae02 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a VPCS instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port where the nio should be added - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a VPCS instance Parameters ********** -- **project_id**: UUID for the project -- **adapter_number**: Network adapter where the nio is located - **port_number**: Port from where the nio should be removed - **vm_id**: UUID for the instance +- **adapter_number**: Network adapter where the nio is located +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidreload.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidreload.rst index 224798fd..0aa2eea1 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidreload.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidreload.rst @@ -9,8 +9,8 @@ Reload a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstart.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstart.rst index b87f43a6..838a512b 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstart.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstart.rst @@ -9,8 +9,8 @@ Start a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstop.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstop.rst index 269c8953..606cc8fb 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstop.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidstop.rst @@ -9,8 +9,8 @@ Stop a VPCS instance Parameters ********** -- **project_id**: UUID for the project - **vm_id**: UUID for the instance +- **project_id**: UUID for the project Response status codes ********************** diff --git a/tests/modules/iou/test_iou_vm.py b/tests/modules/iou/test_iou_vm.py index 398c2215..fab4d48f 100644 --- a/tests/modules/iou/test_iou_vm.py +++ b/tests/modules/iou/test_iou_vm.py @@ -342,7 +342,7 @@ def test_enable_l1_keepalives(loop, vm): def test_start_capture(vm, tmpdir, manager, free_console_port, loop): output_file = str(tmpdir / "test.pcap") - nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"}) + nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) vm.adapter_add_nio_binding(0, 0, nio) loop.run_until_complete(asyncio.async(vm.start_capture(0, 0, output_file))) assert vm._adapters[0].get_nio(0).capturing @@ -351,7 +351,7 @@ def test_start_capture(vm, tmpdir, manager, free_console_port, loop): def test_stop_capture(vm, tmpdir, manager, free_console_port, loop): output_file = str(tmpdir / "test.pcap") - nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"}) + nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) vm.adapter_add_nio_binding(0, 0, nio) loop.run_until_complete(vm.start_capture(0, 0, output_file)) assert vm._adapters[0].get_nio(0).capturing From e9ec5c8a37e35857a76e3db80f0b13d812a69097 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 4 Mar 2015 16:01:56 +0100 Subject: [PATCH 004/336] A notification stream with process monitoring --- gns3server/handlers/api/project_handler.py | 56 ++++++++++++++++++++- gns3server/modules/base_vm.py | 13 +++++ gns3server/modules/dynamips/hypervisor.py | 10 ++++ gns3server/modules/dynamips/nodes/router.py | 17 ++++++- gns3server/modules/iou/iou_vm.py | 42 +++++++++++----- gns3server/modules/iou/ioucon.py | 1 - gns3server/modules/project.py | 26 ++++++++++ gns3server/modules/qemu/qemu_vm.py | 22 ++++++-- gns3server/modules/vpcs/vpcs_vm.py | 18 ++++++- gns3server/schemas/vpcs.py | 6 ++- gns3server/utils/asyncio.py | 18 +++++++ tests/handlers/api/test_project.py | 36 +++++++++++++ tests/handlers/api/test_vpcs.py | 4 +- tests/modules/vpcs/test_vpcs_vm.py | 10 ++++ tox.ini | 2 +- 15 files changed, 256 insertions(+), 25 deletions(-) diff --git a/gns3server/handlers/api/project_handler.py b/gns3server/handlers/api/project_handler.py index e31d32bc..74744a45 100644 --- a/gns3server/handlers/api/project_handler.py +++ b/gns3server/handlers/api/project_handler.py @@ -15,14 +15,23 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import asyncio +import json + from ...web.route import Route from ...schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA, PROJECT_UPDATE_SCHEMA from ...modules.project_manager import ProjectManager from ...modules import MODULES +import logging +log = logging.getLogger() + class ProjectHandler: + # How many clients has subcribe to notifications + _notifications_listening = 0 + @classmethod @Route.post( r"/projects", @@ -123,8 +132,9 @@ class ProjectHandler: pm = ProjectManager.instance() project = pm.get_project(request.match_info["project_id"]) - yield from project.close() - pm.remove_project(project.id) + if ProjectHandler._notifications_listening == 0: + yield from project.close() + pm.remove_project(project.id) response.set_status(204) @classmethod @@ -145,3 +155,45 @@ class ProjectHandler: yield from project.delete() pm.remove_project(project.id) response.set_status(204) + + @classmethod + @Route.get( + r"/projects/{project_id}/notifications", + description="Receive notifications about the projects", + parameters={ + "project_id": "The UUID of the project", + }, + status_codes={ + 200: "End of stream", + 404: "The project doesn't exist" + }) + def notification(request, response): + + pm = ProjectManager.instance() + project = pm.get_project(request.match_info["project_id"]) + + 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 + response.content_length = None + + response.start(request) + queue = project.get_listen_queue() + ProjectHandler._notifications_listening += 1 + response.write("{\"action\": \"ping\"}\n".encode("utf-8")) + while True: + try: + (action, msg) = yield from asyncio.wait_for(queue.get(), 5) + if hasattr(msg, "__json__"): + msg = json.dumps({"action": action, "event": msg.__json__()}, sort_keys=True) + else: + msg = json.dumps({"action": action, "event": msg}, sort_keys=True) + log.debug("Send notification: %s", msg) + response.write(("{}\n".format(msg)).encode("utf-8")) + except asyncio.futures.CancelledError as e: + break + except asyncio.futures.TimeoutError as e: + response.write("{\"action\": \"ping\"}\n".encode("utf-8")) + project.stop_listen_queue(queue) + ProjectHandler._notifications_listening -= 1 diff --git a/gns3server/modules/base_vm.py b/gns3server/modules/base_vm.py index ffea595e..f9dbc2df 100644 --- a/gns3server/modules/base_vm.py +++ b/gns3server/modules/base_vm.py @@ -48,6 +48,7 @@ class BaseVM: self._manager = manager self._console = console self._temporary_directory = None + self._vm_status = "stopped" if self._console is not None: self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project) @@ -66,6 +67,18 @@ class BaseVM: if os.path.exists(self._temporary_directory): shutil.rmtree(self._temporary_directory, ignore_errors=True) + @property + def status(self): + """Return current VM status""" + + return self._vm_status + + @status.setter + def status(self, status): + + self._vm_status = status + self._project.emit("vm.{}".format(status), self) + @property def project(self): """ diff --git a/gns3server/modules/dynamips/hypervisor.py b/gns3server/modules/dynamips/hypervisor.py index a568ff51..10349019 100644 --- a/gns3server/modules/dynamips/hypervisor.py +++ b/gns3server/modules/dynamips/hypervisor.py @@ -70,6 +70,16 @@ class Hypervisor(DynamipsHypervisor): return self._id + @property + def process(self): + """ + Returns the subprocess of the Hypervisor + + :returns: subprocess + """ + + return self._process + @property def started(self): """ diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 77045290..adb1a1ef 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -35,7 +35,8 @@ from ...base_vm import BaseVM from ..dynamips_error import DynamipsError from ..nios.nio_udp import NIOUDP -from gns3server.utils.asyncio import wait_run_in_executor +from gns3server.config import Config +from gns3server.utils.asyncio import wait_run_in_executor, monitor_process class Router(BaseVM): @@ -162,7 +163,7 @@ class Router(BaseVM): slot_number += 1 # add the wics - if self._slots[0] and self._slots[0].wics: + if len(self._slots) > 0 and self._slots[0] and self._slots[0].wics: for wic_slot_number in range(0, len(self._slots[0].wics)): if self._slots[0].wics[wic_slot_number]: router_info["wic" + str(wic_slot_number)] = str(self._slots[0].wics[wic_slot_number]) @@ -251,7 +252,18 @@ class Router(BaseVM): raise DynamipsError('"{}" is not a valid IOS image'.format(self._image)) yield from self._hypervisor.send('vm start "{name}"'.format(name=self._name)) + self.status = "started" log.info('router "{name}" [{id}] has been started'.format(name=self._name, id=self._id)) + monitor_process(self._hypervisor.process, self._termination_callback) + + @asyncio.coroutine + def _termination_callback(self, returncode): + """ + Called when the process is killed + + :param returncode: Process returncode + """ + self.status = "stopped" @asyncio.coroutine def stop(self): @@ -262,6 +274,7 @@ class Router(BaseVM): status = yield from self.get_status() if status != "inactive": yield from self._hypervisor.send('vm stop "{name}"'.format(name=self._name)) + self.status = "stopped" log.info('Router "{name}" [{id}] has been stopped'.format(name=self._name, id=self._id)) @asyncio.coroutine diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index b7214bd4..3e86e8aa 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -465,6 +465,8 @@ class IOUVM(BaseVM): env=env) log.info("IOU instance {} started PID={}".format(self._id, self._iou_process.pid)) self._started = True + self.status = "started" + gns3server.utils.asyncio.monitor_process(self._iou_process, self._termination_callback) except FileNotFoundError as e: raise IOUError("Could not start IOU: {}: 32-bit binary support is probably not installed".format(e)) except (OSError, subprocess.SubprocessError) as e: @@ -477,6 +479,17 @@ class IOUVM(BaseVM): # connections support yield from self._start_iouyap() + def _termination_callback(self, returncode): + """ + Called when the process is killed + + :param returncode: Process returncode + """ + log.info("IOU process crash return code: %d", returncode) + self._terminate_process_iou() + self._terminate_process_iouyap() + self._ioucon_thread_stop_event.set() + def _rename_nvram_file(self): """ Before starting the VM, rename the nvram and vlan.dat files with the correct IOU application identifier. @@ -507,6 +520,7 @@ class IOUVM(BaseVM): stderr=subprocess.STDOUT, cwd=self.working_dir) + gns3server.utils.asyncio.monitor_process(self._iouyap_process, self._termination_callback) log.info("iouyap started PID={}".format(self._iouyap_process.pid)) except (OSError, subprocess.SubprocessError) as e: iouyap_stdout = self.read_iouyap_stdout() @@ -615,24 +629,28 @@ class IOUVM(BaseVM): Terminate the IOUYAP process if running. """ - log.info('Stopping IOUYAP process for IOU VM "{}" PID={}'.format(self.name, self._iouyap_process.pid)) - try: - self._iouyap_process.terminate() - # Sometime the process may already be dead when we garbage collect - except ProcessLookupError: - pass + if self._iouyap_process: + log.info('Stopping IOUYAP process for IOU VM "{}" PID={}'.format(self.name, self._iouyap_process.pid)) + try: + self._iouyap_process.terminate() + # Sometime the process can already be dead when we garbage collect + except ProcessLookupError: + pass def _terminate_process_iou(self): """ Terminate the IOU process if running """ - log.info('Stopping IOU process for IOU VM "{}" PID={}'.format(self.name, self._iou_process.pid)) - try: - self._iou_process.terminate() - # Sometime the process may already be dead when we garbage collect - except ProcessLookupError: - pass + if self._iou_process: + log.info('Stopping IOU process for IOU VM "{}" PID={}'.format(self.name, self._iou_process.pid)) + try: + self._iou_process.terminate() + # Sometime the process can already be dead when we garbage collect + except ProcessLookupError: + pass + self._started = False + self.status = "stopped" @asyncio.coroutine def reload(self): diff --git a/gns3server/modules/iou/ioucon.py b/gns3server/modules/iou/ioucon.py index 764e81e8..475109b6 100644 --- a/gns3server/modules/iou/ioucon.py +++ b/gns3server/modules/iou/ioucon.py @@ -541,7 +541,6 @@ def send_recv_loop(epoll, console, router, esc_char, stop_event): else: router.write(buf) finally: - log.debug("Finally") router.unregister(epoll) console.unregister(epoll) diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index edb1b6cf..8639b8ce 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -65,6 +65,9 @@ class Project: self._used_tcp_ports = set() self._used_udp_ports = set() + # List of clients listen for notifications + self._listeners = set() + if path is None: path = os.path.join(self._location, self._id) try: @@ -416,3 +419,26 @@ class Project: # We import it at the last time to avoid circular dependencies from ..modules import MODULES return MODULES + + def emit(self, action, event): + """ + Send an event to all the client listens for notifications + + :param action: Action happened + :param event: Event sended to the client + """ + for listener in self._listeners: + listener.put_nowait((action, event, )) + + def get_listen_queue(self): + """Get a queue where you receive all the events related to the + project.""" + + queue = asyncio.Queue() + self._listeners.add(queue) + return queue + + def stop_listen_queue(self, queue): + """Stop sending notification to this clients""" + + self._listeners.remove(queue) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 85788a80..cfcbf2c8 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -35,6 +35,8 @@ from ..nios.nio_udp import NIOUDP from ..nios.nio_tap import NIOTAP from ..base_vm import BaseVM from ...schemas.qemu import QEMU_OBJECT_SCHEMA +from ...utils.asyncio import monitor_process +from ...config import Config import logging log = logging.getLogger(__name__) @@ -62,7 +64,6 @@ class QemuVM(BaseVM): self._host = server_config.get("host", "127.0.0.1") self._monitor_host = server_config.get("monitor_host", "127.0.0.1") self._command = [] - self._started = False self._process = None self._cpulimit_process = None self._monitor = None @@ -581,7 +582,9 @@ class QemuVM(BaseVM): stderr=subprocess.STDOUT, cwd=self.working_dir) log.info('QEMU VM "{}" started PID={}'.format(self._name, self._process.pid)) - self._started = True + + self.status = "started" + monitor_process(self._process, self._termination_callback) except (OSError, subprocess.SubprocessError) as e: stdout = self.read_stdout() log.error("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout)) @@ -591,6 +594,17 @@ class QemuVM(BaseVM): if self._cpu_throttling: self._set_cpu_throttling() + def _termination_callback(self, returncode): + """ + Called when the process is killed + + :param returncode: Process returncode + """ + if self.started: + log.info("Process Qemu is dead. Return code: %d", returncode) + self.status = "stopped" + self._process = None + @asyncio.coroutine def stop(self): """ @@ -608,7 +622,7 @@ class QemuVM(BaseVM): if self._process.returncode is None: log.warn('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid)) self._process = None - self._started = False + self.status = "stopped" self._stop_cpulimit() @asyncio.coroutine @@ -807,7 +821,7 @@ class QemuVM(BaseVM): :returns: boolean """ - return self._started + return self.status == "started" def read_stdout(self): """ diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index cb7229d7..ddb97865 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -35,7 +35,7 @@ from ..adapters.ethernet_adapter import EthernetAdapter from ..nios.nio_udp import NIOUDP from ..nios.nio_tap import NIOTAP from ..base_vm import BaseVM -from ...utils.asyncio import subprocess_check_output +from ...utils.asyncio import subprocess_check_output, monitor_process import logging @@ -109,6 +109,7 @@ class VPCSVM(BaseVM): return {"name": self.name, "vm_id": self.id, + "status": self.status, "console": self._console, "project_id": self.project.id, "startup_script": self.startup_script, @@ -233,13 +234,27 @@ class VPCSVM(BaseVM): stderr=subprocess.STDOUT, cwd=self.working_dir, creationflags=flags) + monitor_process(self._process, self._termination_callback) log.info("VPCS instance {} started PID={}".format(self.name, self._process.pid)) self._started = True + self.status = "started" except (OSError, subprocess.SubprocessError) as e: vpcs_stdout = self.read_vpcs_stdout() log.error("Could not start VPCS {}: {}\n{}".format(self.vpcs_path, e, vpcs_stdout)) raise VPCSError("Could not start VPCS {}: {}\n{}".format(self.vpcs_path, e, vpcs_stdout)) + def _termination_callback(self, returncode): + """ + Called when the process is killed + + :param returncode: Process returncode + """ + if self._started: + log.info("Process VPCS is dead. Return code: %d", returncode) + self._started = False + self.status = "stopped" + self._process = None + @asyncio.coroutine def stop(self): """ @@ -258,6 +273,7 @@ class VPCSVM(BaseVM): self._process = None self._started = False + self.status = "stopped" @asyncio.coroutine def reload(self): diff --git a/gns3server/schemas/vpcs.py b/gns3server/schemas/vpcs.py index 05d60d98..c2dba3d9 100644 --- a/gns3server/schemas/vpcs.py +++ b/gns3server/schemas/vpcs.py @@ -92,6 +92,10 @@ VPCS_OBJECT_SCHEMA = { "maxLength": 36, "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" }, + "status": { + "description": "VM status", + "enum": ["started", "stopped"] + }, "console": { "description": "console TCP port", "minimum": 1, @@ -115,5 +119,5 @@ VPCS_OBJECT_SCHEMA = { }, }, "additionalProperties": False, - "required": ["name", "vm_id", "console", "project_id", "startup_script_path"] + "required": ["name", "vm_id", "status", "console", "project_id", "startup_script_path"] } diff --git a/gns3server/utils/asyncio.py b/gns3server/utils/asyncio.py index c84e0fbe..7c9cc606 100644 --- a/gns3server/utils/asyncio.py +++ b/gns3server/utils/asyncio.py @@ -17,6 +17,8 @@ import asyncio +import shutil +import sys @asyncio.coroutine @@ -76,3 +78,19 @@ def wait_for_process_termination(process, timeout=10): yield from asyncio.sleep(0.1) timeout -= 0.1 raise asyncio.TimeoutError() + + +@asyncio.coroutine +def _check_process(process, termination_callback): + if not hasattr(sys, "_called_from_test") or not sys._called_from_test: + returncode = yield from process.wait() + if asyncio.iscoroutinefunction(termination_callback): + yield from termination_callback(returncode) + else: + termination_callback(returncode) + + +def monitor_process(process, termination_callback): + """Call termination_callback when process die""" + + asyncio.async(_check_process(process, termination_callback)) diff --git a/tests/handlers/api/test_project.py b/tests/handlers/api/test_project.py index cd0bc419..604ec756 100644 --- a/tests/handlers/api/test_project.py +++ b/tests/handlers/api/test_project.py @@ -20,9 +20,13 @@ This test suite check /project endpoint """ import uuid +import asyncio +import aiohttp from unittest.mock import patch from tests.utils import asyncio_patch +from gns3server.handlers.api.project_handler import ProjectHandler + def test_create_project_with_path(server, tmpdir): with patch("gns3server.modules.project.Project.is_local", return_value=True): @@ -139,6 +143,38 @@ def test_close_project(server, project): assert mock.called +def test_close_project_two_client_connected(server, project): + + ProjectHandler._notifications_listening = 2 + + with asyncio_patch("gns3server.modules.project.Project.close", return_value=True) as mock: + response = server.post("/projects/{project_id}/close".format(project_id=project.id), example=True) + assert response.status == 204 + assert not mock.called + + def test_close_project_invalid_uuid(server): response = server.post("/projects/{project_id}/close".format(project_id=uuid.uuid4())) assert response.status == 404 + + +def test_notification(server, project, loop): + @asyncio.coroutine + def go(future): + response = yield from aiohttp.request("GET", server.get_url("/projects/{project_id}/notifications".format(project_id=project.id), 1)) + response.body = yield from response.content.read(19) + project.emit("vm.created", {"a": "b"}) + response.body += yield from response.content.read(47) + response.close() + future.set_result(response) + + future = asyncio.Future() + asyncio.async(go(future)) + response = loop.run_until_complete(future) + assert response.status == 200 + assert response.body == b'{"action": "ping"}\n{"action": "vm.created", "event": {"a": "b"}}\n' + + +def test_notification_invalid_id(server, project): + response = server.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4())) + assert response.status == 404 diff --git a/tests/handlers/api/test_vpcs.py b/tests/handlers/api/test_vpcs.py index 473a533e..5970306e 100644 --- a/tests/handlers/api/test_vpcs.py +++ b/tests/handlers/api/test_vpcs.py @@ -42,7 +42,8 @@ def test_vpcs_get(server, project, vm): assert response.route == "/projects/{project_id}/vpcs/vms/{vm_id}" assert response.json["name"] == "PC TEST 1" assert response.json["project_id"] == project.id - assert response.json["startup_script_path"] == None + assert response.json["startup_script_path"] is None + assert response.json["status"] == "stopped" def test_vpcs_create_startup_script(server, project): @@ -95,6 +96,7 @@ def test_vpcs_delete_nio(server, vm): def test_vpcs_start(server, vm): + with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock: response = server.post("/projects/{project_id}/vpcs/vms/{vm_id}/start".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True) assert mock.called diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index c16e10f0..a33d4397 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -72,6 +72,7 @@ def test_vm_invalid_vpcs_path(project, manager, loop): def test_start(loop, vm): process = MagicMock() process.returncode = None + queue = vm.project.get_listen_queue() with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): @@ -79,6 +80,9 @@ def test_start(loop, vm): vm.port_add_nio_binding(0, nio) loop.run_until_complete(asyncio.async(vm.start())) assert vm.is_running() + (action, event) = queue.get_nowait() + assert action == "vm.started" + assert event == vm def test_stop(loop, vm): @@ -98,6 +102,8 @@ def test_stop(loop, vm): loop.run_until_complete(asyncio.async(vm.start())) assert vm.is_running() + queue = vm.project.get_listen_queue() + with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): loop.run_until_complete(asyncio.async(vm.stop())) assert vm.is_running() is False @@ -107,6 +113,10 @@ def test_stop(loop, vm): else: process.terminate.assert_called_with() + (action, event) = queue.get_nowait() + assert action == "vm.stopped" + assert event == vm + def test_reload(loop, vm): process = MagicMock() diff --git a/tox.ini b/tox.ini index 155cefab..8c8c31ae 100644 --- a/tox.ini +++ b/tox.ini @@ -10,4 +10,4 @@ ignore = E501,E402 [pytest] norecursedirs = old_tests .tox -timeout = 2 +timeout = 5 From a260377f0e05a1f93376205415b14eb7d94592cd Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 1 May 2015 18:47:46 -0600 Subject: [PATCH 005/336] List all available VMware VMs. --- gns3server/handlers/api/vmware_handler.py | 27 ++++++++++++++------ gns3server/modules/vmware/__init__.py | 30 +++++++++++++++++++++-- gns3server/modules/vmware/vmware_error.py | 2 +- gns3server/modules/vmware/vmware_vm.py | 11 ++++++--- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/gns3server/handlers/api/vmware_handler.py b/gns3server/handlers/api/vmware_handler.py index e8bf35e0..65b39316 100644 --- a/gns3server/handlers/api/vmware_handler.py +++ b/gns3server/handlers/api/vmware_handler.py @@ -29,6 +29,19 @@ class VMwareHandler: API entry points for VMware. """ + @classmethod + @Route.get( + r"/vmware/vms", + status_codes={ + 200: "Success", + }, + description="Get all VMware VMs available") + def show(request, response): + + vmware_manager = VMware.instance() + vms = vmware_manager.list_vms() + response.json(vms) + @classmethod @Route.post( r"/projects/{project_id}/vmware/vms", @@ -53,10 +66,10 @@ class VMwareHandler: request.json.pop("linked_clone"), console=request.json.get("console", None)) - # for name, value in request.json.items(): - # if name != "vm_id": - # if hasattr(vm, name) and getattr(vm, name) != value: - # setattr(vm, name, value) + for name, value in request.json.items(): + if name != "vm_id": + if hasattr(vm, name) and getattr(vm, name) != value: + setattr(vm, name, value) response.set_status(201) response.json(vm) @@ -102,9 +115,9 @@ class VMwareHandler: vmware_manager = VMware.instance() vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) - # for name, value in request.json.items(): - # if hasattr(vm, name) and getattr(vm, name) != value: - # setattr(vm, name, value) + for name, value in request.json.items(): + if hasattr(vm, name) and getattr(vm, name) != value: + setattr(vm, name, value) response.json(vm) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 6d876430..6263eaab 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -41,7 +41,7 @@ class VMware(BaseManager): super().__init__() self._vmrun_path = None - self._host_type = "player" + self._default_vm_path = "/home/grossmj/vmware" @property def vmrun_path(self): @@ -84,7 +84,10 @@ class VMware(BaseManager): if not vmrun_path: vmrun_path = self.find_vmrun() if host_type is None: - host_type = self._host_type + if sys.platform.startswith("darwin"): + host_type = "fusion" + else: + host_type = self.config.get_section_config("VMware").get("host_type", "ws") command = [vmrun_path, "-T", host_type, subcommand] command.extend(args) log.debug("Executing vmrun with command: {}".format(command)) @@ -104,3 +107,26 @@ class VMware(BaseManager): raise VMwareError("vmrun has returned an error: {}".format(vmrun_error)) return stdout_data.decode("utf-8", errors="ignore").splitlines() + + def list_vms(self): + """ + Gets VMware VM list. + """ + + vms = [] + for path, _, filenames in os.walk(os.path.expanduser("~/vmware")): + for filename in filenames: + if os.path.splitext(filename)[1] == ".vmx": + vmx_path = os.path.join(path, filename) + try: + with open(vmx_path, encoding="utf-8") as f: + for line in f.read().splitlines(): + name, value = line.split('=', 1) + if name.strip() == "displayName": + vmname = value.strip('" ') + vms.append({"vmname": vmname, "vmx_path": vmx_path}) + break + except (OSError, ValueError) as e: + log.warning("Could not read VMware vmx file {}: {}".format(vmx_path, e)) + continue + return vms diff --git a/gns3server/modules/vmware/vmware_error.py b/gns3server/modules/vmware/vmware_error.py index 8a254030..88ea393c 100644 --- a/gns3server/modules/vmware/vmware_error.py +++ b/gns3server/modules/vmware/vmware_error.py @@ -16,7 +16,7 @@ # along with this program. If not, see . """ -Custom exceptions for the VirtualBox module. +Custom exceptions for the VMware module. """ from ..vm_error import VMError diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 1bc1feee..89a0554c 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -99,8 +99,8 @@ class VMwareVM(BaseVM): Suspends this VMware VM. """ - yield from self._control_vm("suspend") - log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) + yield from self._control_vm("pause") + log.info("VMware VM '{name}' [{id}] paused".format(name=self.name, id=self.id)) @asyncio.coroutine def resume(self): @@ -108,7 +108,7 @@ class VMwareVM(BaseVM): Resumes this VMware VM. """ - yield from self.start() + yield from self._control_vm("unpause") log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id)) @asyncio.coroutine @@ -141,7 +141,10 @@ class VMwareVM(BaseVM): # if nio and isinstance(nio, NIOUDP): # self.manager.port_manager.release_udp_port(nio.lport, self._project) - yield from self.stop() + try: + yield from self.stop() + except VMwareError: + pass log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id)) self._closed = True From c197b39a68ad85e884bdb251f66cab81ea56256e Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 7 May 2015 16:50:37 -0600 Subject: [PATCH 006/336] Find VMware VMs using the inventory and alternatively using the default vm location (for VMware player). --- gns3server/modules/vmware/__init__.py | 75 ++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 6263eaab..f7fb69f0 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -41,7 +41,6 @@ class VMware(BaseManager): super().__init__() self._vmrun_path = None - self._default_vm_path = "/home/grossmj/vmware" @property def vmrun_path(self): @@ -108,25 +107,77 @@ class VMware(BaseManager): return stdout_data.decode("utf-8", errors="ignore").splitlines() - def list_vms(self): - """ - Gets VMware VM list. - """ + def _get_vms_from_inventory(self, inventory_path): + vm_entries = {} vms = [] - for path, _, filenames in os.walk(os.path.expanduser("~/vmware")): + try: + with open(inventory_path, encoding="utf-8") as f: + for line in f.read().splitlines(): + try: + name, value = line.split('=', 1) + vm_entry, variable_name = name.split('.', 1) + if vm_entry.startswith("vmlist"): + if not vm_entry in vm_entries: + vm_entries[vm_entry] = {} + vm_entries[vm_entry][variable_name.strip()] = value.strip('" ') + except ValueError: + continue + except OSError as e: + log.warning("Could not read VMware inventory file {}: {}".format(inventory_path, e)) + + for vm_settings in vm_entries.values(): + if "DisplayName" in vm_settings and "config" in vm_settings: + vms.append({"vmname": vm_settings["DisplayName"], "vmx_path": vm_settings["config"]}) + + return vms + + def _get_vms_from_default_folder(self, folder): + + vms = [] + for path, _, filenames in os.walk(folder): for filename in filenames: if os.path.splitext(filename)[1] == ".vmx": vmx_path = os.path.join(path, filename) try: with open(vmx_path, encoding="utf-8") as f: for line in f.read().splitlines(): - name, value = line.split('=', 1) - if name.strip() == "displayName": - vmname = value.strip('" ') - vms.append({"vmname": vmname, "vmx_path": vmx_path}) - break - except (OSError, ValueError) as e: + try: + name, value = line.split('=', 1) + if name.strip() == "displayName": + vmname = value.strip('" ') + vms.append({"vmname": vmname, "vmx_path": vmx_path}) + break + except ValueError: + continue + except OSError as e: log.warning("Could not read VMware vmx file {}: {}".format(vmx_path, e)) continue return vms + + def list_vms(self): + """ + Gets VMware VM list. + """ + + if sys.platform.startswith("win"): + inventory_path = os.path.expandvars(r"%APPDATA%\Vmware\Inventory.vmls") + elif sys.platform.startswith("darwin"): + inventory_path = os.path.expanduser("~/Library/Application\ Support/VMware Fusion/vmInventory") + else: + inventory_path = os.path.expanduser("~/.vmware/inventory.vmls") + + if os.path.exists(inventory_path): + return self._get_vms_from_inventory(inventory_path) + else: + # VMware player has no inventory file, let's use the default location for VMs. + # TODO: default location can be changed in the preferences file (prefvmx.defaultvmpath = "path") + # Windows: %APPDATA%\Vmware\preferences.ini + # Linux: ~/.vmware/preferences + if sys.platform.startswith("win"): + default_vm_path = os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") + else: + default_vm_path = os.path.expanduser("~/vmware") # FIXME: check if correct path on OSX + if os.path.isdir(default_vm_path): + return self._get_vms_from_default_folder(default_vm_path) + log.warning("Default VMware VM location doesn't exist: {}".format(default_vm_path)) From 91ff6e5ae93d41797d9a3fccfd373eaeb55eaba4 Mon Sep 17 00:00:00 2001 From: Vasil Rangelov Date: Sat, 9 May 2015 01:33:06 +0300 Subject: [PATCH 007/336] Updated compilation instructions for Windows. Synced up requirements.txt and setup.py dependencies. --- README.rst | 41 ++++++++++++++++++++++++++++++++++++----- requirements.txt | 10 ++++------ setup.py | 17 ++++++++--------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index 23fb30bb..d8bb0b43 100644 --- a/README.rst +++ b/README.rst @@ -20,16 +20,17 @@ Branches master ****** master is the next stable release, you can test it in your day to day activities. -Bug fixes or small improvements pull requests goes here. +Bug fixes or small improvements pull requests go here. unstable ******** -*Never* use this branch for production. Major new features pull requests goes here. +*Never* use this branch for production. Pull requests for major new features go here. Linux ----- GNS3 is perhaps packaged for your distribution: + * Gentoo: https://packages.gentoo.org/package/net-misc/gns3-server @@ -76,6 +77,7 @@ You will found init sample script for various systems inside the init directory. Usefull options: + * --daemon: start process as a daemon * --log logfile: store output in a logfile * --pid pidfile: store the pid of the running process in a file and prevent double execution @@ -96,10 +98,39 @@ You need to copy init/gns3.conf.upstart to /etc/init/gns3.conf Windows ------- -Please use our all-in-one installer. -If you install it via source you need to install also: -https://sourceforge.net/projects/pywin32/ +Please use our `all-in-one installer `_ to install the stable build. + +If you install via source you need to first install: + +- Python (3.3 or above) - https://www.python.org/downloads/windows/ +- Pywin32 - https://sourceforge.net/projects/pywin32/ + +Then you can call + +.. code:: bash + + python setup.py install + +to install the remaining dependencies. + +To run the tests, you also need to call + +.. code:: bash + + pip install pytest pytest-capturelog + +before actually running the tests with + +.. code:: bash + + python setup.py test + +or with + +.. code:: bash + + py.test -v Mac OS X -------- diff --git a/requirements.txt b/requirements.txt index 26ea3a4f..34a020ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ -netifaces==0.10.4 -jsonschema==2.4.0 -python-dateutil==2.3 -aiohttp==0.15.1 -Jinja2==2.7.3 -raven==5.2.0 +jsonschema>=2.4.0 +aiohttp>=0.15.1 +Jinja2>=2.7.3 +raven>=5.2.0 diff --git a/setup.py b/setup.py index 01b853e8..ddb3a140 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,6 @@ from setuptools.command.test import test as TestCommand class PyTest(TestCommand): - def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] @@ -30,17 +29,17 @@ class PyTest(TestCommand): def run_tests(self): # import here, cause outside the eggs aren't loaded import pytest + errcode = pytest.main(self.test_args) sys.exit(errcode) -dependencies = ["aiohttp>=0.15.1", - "jsonschema>=2.4.0", - "Jinja2>=2.7.3", - "raven>=5.2.0"] - -#if not sys.platform.startswith("win"): -# dependencies.append("netifaces==0.10.4") +dependencies = [ + "jsonschema>=2.4.0", + "aiohttp>=0.15.1", + "Jinja2>=2.7.3", + "raven>=5.2.0" +] if sys.version_info == (3, 3): dependencies.append("asyncio>=3.4.2") @@ -50,7 +49,7 @@ setup( version=__import__("gns3server").__version__, url="http://github.com/GNS3/gns3-server", license="GNU General Public License v3 (GPLv3)", - tests_require=["pytest"], + tests_require=["pytest", "pytest-capturelog"], cmdclass={"test": PyTest}, description="GNS3 server", long_description=open("README.rst", "r").read(), From 13eb461dfe67c80f3c97e83820006ede17c20367 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 8 May 2015 18:30:41 -0600 Subject: [PATCH 008/336] Update default VMware VM location path for OSX. --- gns3server/modules/vmware/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index f7fb69f0..9219db60 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -174,10 +174,13 @@ class VMware(BaseManager): # TODO: default location can be changed in the preferences file (prefvmx.defaultvmpath = "path") # Windows: %APPDATA%\Vmware\preferences.ini # Linux: ~/.vmware/preferences + # OSX: ~/Library/Preferences/VMware Fusion/preferences if sys.platform.startswith("win"): default_vm_path = os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") + elif sys.platform.startswith("darwin"): + default_vm_path = os.path.expanduser("~/Documents/Virtual Machines.localized") else: - default_vm_path = os.path.expanduser("~/vmware") # FIXME: check if correct path on OSX + default_vm_path = os.path.expanduser("~/vmware") if os.path.isdir(default_vm_path): return self._get_vms_from_default_folder(default_vm_path) log.warning("Default VMware VM location doesn't exist: {}".format(default_vm_path)) From bb4ebbfe64afb853e23ef3a6e52f3e35cea9027f Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 24 Apr 2015 10:15:23 +0200 Subject: [PATCH 009/336] Upload images API Conflicts: requirements.txt setup.py --- .../handlers/api/dynamips_vm_handler.py | 13 ++++++++++ gns3server/handlers/api/iou_handler.py | 13 ++++++++++ gns3server/handlers/api/qemu_handler.py | 13 ++++++++++ gns3server/modules/base_manager.py | 17 ++++++++++++ gns3server/web/route.py | 4 ++- setup.py | 1 + tests/handlers/api/test_dynamips.py | 19 ++++++++++++++ tests/handlers/api/test_iou.py | 26 ++++++++++++++++--- tests/handlers/api/test_qemu.py | 19 ++++++++++++++ 9 files changed, 121 insertions(+), 4 deletions(-) diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py index 75b197dc..6d46fc86 100644 --- a/gns3server/handlers/api/dynamips_vm_handler.py +++ b/gns3server/handlers/api/dynamips_vm_handler.py @@ -463,3 +463,16 @@ class DynamipsVMHandler: vms = yield from dynamips_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/dynamips/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload Dynamips image.") + def upload_vm(request, response): + + dynamips_manager = Dynamips.instance() + yield from dynamips_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/handlers/api/iou_handler.py b/gns3server/handlers/api/iou_handler.py index d87e7537..520decc2 100644 --- a/gns3server/handlers/api/iou_handler.py +++ b/gns3server/handlers/api/iou_handler.py @@ -330,3 +330,16 @@ class IOUHandler: vms = yield from iou_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/iou/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload IOU image.") + def upload_vm(request, response): + + iou_manager = IOU.instance() + yield from iou_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index a703cdb1..041525d1 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -305,3 +305,16 @@ class QEMUHandler: vms = yield from qemu_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/qemu/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload Qemu image.") + def upload_vm(request, response): + + qemu_manager = Qemu.instance() + yield from qemu_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index b7f87f8b..674eb419 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -444,3 +444,20 @@ class BaseManager: """ raise NotImplementedError + + @asyncio.coroutine + def write_image(self, filename, stream): + directory = self.get_images_directory() + path = os.path.join(directory, os.path.basename(filename)) + log.info("Writting image file %s", path) + try: + os.makedirs(directory, exist_ok=True) + with open(path, 'wb+') as f: + while True: + packet = yield from stream.read(512) + if not packet: + break + f.write(packet) + os.chmod(path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) + except OSError as e: + raise aiohttp.web.HTTPConflict(text="Could not write image: {} to {}".format(filename, e)) diff --git a/gns3server/web/route.py b/gns3server/web/route.py index f376b820..7601bd6b 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -89,6 +89,7 @@ class Route(object): output_schema = kw.get("output", {}) input_schema = kw.get("input", {}) api_version = kw.get("api_version", 1) + raw = kw.get("raw", False) # If it's a JSON api endpoint just register the endpoint an do nothing if api_version is None: @@ -119,8 +120,9 @@ class Route(object): # This block is executed at each method call # Non API call - if api_version is None: + if api_version is None or raw is True: response = Response(request=request, route=route, output_schema=output_schema) + yield from func(request, response) return response diff --git a/setup.py b/setup.py index ddb3a140..5f4b54cd 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ dependencies = [ "raven>=5.2.0" ] + if sys.version_info == (3, 3): dependencies.append("asyncio>=3.4.2") diff --git a/tests/handlers/api/test_dynamips.py b/tests/handlers/api/test_dynamips.py index 8b096071..9deb65af 100644 --- a/tests/handlers/api/test_dynamips.py +++ b/tests/handlers/api/test_dynamips.py @@ -146,3 +146,22 @@ def test_vms(server, tmpdir, fake_dynamips): response = server.get("/dynamips/vms") assert response.status == 200 assert response.json == [{"filename": "7200.bin"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.Dynamips.get_images_directory", return_value=str(tmpdir),): + response = server.post("/dynamips/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.Dynamips.get_images_directory", return_value=str(tmpdir),): + response = server.post("/dynamips/vms/test2", body="TEST", raw=True) + assert response.status == 409 diff --git a/tests/handlers/api/test_iou.py b/tests/handlers/api/test_iou.py index 7b91ef62..962cd81d 100644 --- a/tests/handlers/api/test_iou.py +++ b/tests/handlers/api/test_iou.py @@ -20,6 +20,7 @@ import os import stat import sys import uuid +import aiohttp from tests.utils import asyncio_patch from unittest.mock import patch, MagicMock, PropertyMock @@ -311,9 +312,28 @@ def test_get_initial_config_with_config_file(server, project, vm): assert response.json["content"] == "TEST" -def test_vms(server, vm, tmpdir, fake_iou_bin): +def test_vms(server, tmpdir, fake_iou_bin): - with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir), example=True): - response = server.get("/iou/vms") + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir)): + response = server.get("/iou/vms", example=True) assert response.status == 200 assert response.json == [{"filename": "iou.bin"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir),): + response = server.post("/iou/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir),): + response = server.post("/iou/vms/test2", body="TEST", raw=True) + assert response.status == 409 diff --git a/tests/handlers/api/test_qemu.py b/tests/handlers/api/test_qemu.py index 4af9065f..cf08992a 100644 --- a/tests/handlers/api/test_qemu.py +++ b/tests/handlers/api/test_qemu.py @@ -200,3 +200,22 @@ def test_vms(server, tmpdir, fake_qemu_vm): response = server.get("/qemu/vms") assert response.status == 200 assert response.json == [{"filename": "linux.img"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.Qemu.get_images_directory", return_value=str(tmpdir),): + response = server.post("/qemu/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.Qemu.get_images_directory", return_value=str(tmpdir),): + response = server.post("/qemu/vms/test2", body="TEST", raw=True) + assert response.status == 409 From 8e168dfbe0644ca015788c679d6e6b2903fabf5f Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 10 May 2015 17:21:31 -0600 Subject: [PATCH 010/336] Completes VMware VMs searching process. --- gns3server/modules/vmware/__init__.py | 105 ++++++++++++++++++-------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 9219db60..49e7bbee 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -53,12 +53,17 @@ class VMware(BaseManager): return self._vmrun_path def find_vmrun(self): + """ + Searches for vmrun. + + :returns: path to vmrun + """ # look for vmrun vmrun_path = self.config.get_section_config("VMware").get("vmrun_path") if not vmrun_path: if sys.platform.startswith("win"): - pass # TODO: use registry to find vmrun + pass # TODO: use registry to find vmrun or search for default location elif sys.platform.startswith("darwin"): vmrun_path = "/Applications/VMware Fusion.app/Contents/Library/vmrun" else: @@ -107,51 +112,80 @@ class VMware(BaseManager): return stdout_data.decode("utf-8", errors="ignore").splitlines() + @staticmethod + def _parse_vmware_file(path): + """ + Parses a VMware file (VMX, preferences or inventory). + + :param path: path to the VMware file + + :returns: dict + """ + + pairs = {} + with open(path, encoding="utf-8") as f: + for line in f.read().splitlines(): + try: + key, value = line.split('=', 1) + pairs[key.strip()] = value.strip('" ') + except ValueError: + continue + return pairs + def _get_vms_from_inventory(self, inventory_path): + """ + Searches for VMs by parsing a VMware inventory file. + + :param inventory_path: path to the inventory file + + :returns: list of VMs + """ vm_entries = {} vms = [] try: - with open(inventory_path, encoding="utf-8") as f: - for line in f.read().splitlines(): + log.debug('Reading VMware inventory file "{}"'.format(inventory_path)) + pairs = self._parse_vmware_file(inventory_path) + for key, value in pairs.items(): + if key.startswith("vmlist"): try: - name, value = line.split('=', 1) - vm_entry, variable_name = name.split('.', 1) - if vm_entry.startswith("vmlist"): - if not vm_entry in vm_entries: - vm_entries[vm_entry] = {} - vm_entries[vm_entry][variable_name.strip()] = value.strip('" ') + vm_entry, variable_name = key.split('.', 1) except ValueError: continue + if not vm_entry in vm_entries: + vm_entries[vm_entry] = {} + vm_entries[vm_entry][variable_name.strip()] = value except OSError as e: log.warning("Could not read VMware inventory file {}: {}".format(inventory_path, e)) for vm_settings in vm_entries.values(): if "DisplayName" in vm_settings and "config" in vm_settings: + log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayName"], vm_settings["config"])) vms.append({"vmname": vm_settings["DisplayName"], "vmx_path": vm_settings["config"]}) - return vms - def _get_vms_from_default_folder(self, folder): + def _get_vms_from_directory(self, directory): + """ + Searches for VMs in a given directory. + + :param directory: path to the directory + + :returns: list of VMs + """ vms = [] - for path, _, filenames in os.walk(folder): + for path, _, filenames in os.walk(directory): for filename in filenames: if os.path.splitext(filename)[1] == ".vmx": vmx_path = os.path.join(path, filename) + log.debug('Reading VMware VMX file "{}"'.format(vmx_path)) try: - with open(vmx_path, encoding="utf-8") as f: - for line in f.read().splitlines(): - try: - name, value = line.split('=', 1) - if name.strip() == "displayName": - vmname = value.strip('" ') - vms.append({"vmname": vmname, "vmx_path": vmx_path}) - break - except ValueError: - continue + pairs = self._parse_vmware_file(vmx_path) + if "displayName" in pairs: + log.debug('Found VM named "{}"'.format(pairs["displayName"])) + vms.append({"vmname": pairs["displayName"], "vmx_path": vmx_path}) except OSError as e: - log.warning("Could not read VMware vmx file {}: {}".format(vmx_path, e)) + log.warning('Could not read VMware VMX file "{}": {}'.format(vmx_path, e)) continue return vms @@ -170,17 +204,26 @@ class VMware(BaseManager): if os.path.exists(inventory_path): return self._get_vms_from_inventory(inventory_path) else: - # VMware player has no inventory file, let's use the default location for VMs. - # TODO: default location can be changed in the preferences file (prefvmx.defaultvmpath = "path") - # Windows: %APPDATA%\Vmware\preferences.ini - # Linux: ~/.vmware/preferences - # OSX: ~/Library/Preferences/VMware Fusion/preferences + # VMware player has no inventory file, let's search the default location for VMs. if sys.platform.startswith("win"): + vmware_preferences_path = os.path.expandvars(r"%APPDATA%\VMware\preferences.ini") default_vm_path = os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") elif sys.platform.startswith("darwin"): + vmware_preferences_path = os.path.expanduser("~/Library/Preferences/VMware Fusion/preferences") default_vm_path = os.path.expanduser("~/Documents/Virtual Machines.localized") else: + vmware_preferences_path = os.path.expanduser("~/.vmware/preferences") default_vm_path = os.path.expanduser("~/vmware") - if os.path.isdir(default_vm_path): - return self._get_vms_from_default_folder(default_vm_path) - log.warning("Default VMware VM location doesn't exist: {}".format(default_vm_path)) + + if os.path.exists(vmware_preferences_path): + # the default vm path may be present in VMware preferences file. + try: + pairs = self._parse_vmware_file(vmware_preferences_path) + if "prefvmx.defaultvmpath" in pairs: + default_vm_path = pairs["prefvmx.defaultvmpath"] + except OSError as e: + log.warning('Could not read VMware preferences file "{}": {}'.format(vmware_preferences_path, e)) + + if not os.path.isdir(default_vm_path): + raise VMwareError('Could not find the default VM directory: "{}"'.format(default_vm_path)) + return self._get_vms_from_directory(default_vm_path) From 5df5a6da18aab62c34a675ca62fea55e5c7e5de3 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 12 May 2015 09:32:42 +0200 Subject: [PATCH 011/336] Fix qemu tests --- tests/modules/qemu/test_qemu_vm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 59dd6c26..36cd9c47 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -272,10 +272,10 @@ def test_build_command(vm, loop, fake_qemu_binary, port_manager): os.path.join(vm.working_dir, "flash.qcow2"), "-serial", "telnet:127.0.0.1:{},server,nowait".format(vm.console), + "-net", + "none", "-device", - "e1000,mac=00:00:ab:0e:0f:00,netdev=gns3-0", - "-netdev", - "user,id=gns3-0" + "e1000,mac=00:00:ab:0e:0f:00", ] From 25a6616cd0a3f93e37f88d9e89847a8c39a99058 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 13 May 2015 13:53:42 -0600 Subject: [PATCH 012/336] Some wording changes. --- gns3server/modules/dynamips/nodes/router.py | 4 +++- gns3server/modules/iou/iou_vm.py | 5 +++-- gns3server/modules/project.py | 8 ++++---- gns3server/modules/qemu/qemu_vm.py | 5 +++-- gns3server/modules/vpcs/vpcs_vm.py | 4 ++-- gns3server/utils/asyncio.py | 3 +-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 25d10e3e..3a6d15af 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -259,11 +259,13 @@ class Router(BaseVM): @asyncio.coroutine def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ + self.status = "stopped" + log.info("Dynamips hypervisor process has stopped, return code: %d", returncode) @asyncio.coroutine def stop(self): diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index 3e86e8aa..ae51f1c9 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -481,11 +481,12 @@ class IOUVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ - log.info("IOU process crash return code: %d", returncode) + + log.info("IOU process has stopped, return code: %d", returncode) self._terminate_process_iou() self._terminate_process_iouyap() self._ioucon_thread_stop_event.set() diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index eb68b333..85f4c7f5 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -65,7 +65,7 @@ class Project: self._used_tcp_ports = set() self._used_udp_ports = set() - # List of clients listen for notifications + # clients listening for notifications self._listeners = set() if path is None: @@ -437,10 +437,10 @@ class Project: def emit(self, action, event): """ - Send an event to all the client listens for notifications + Send an event to all the client listening for notifications - :param action: Action happened - :param event: Event sended to the client + :param action: Action name + :param event: Event to send """ for listener in self._listeners: listener.put_nowait((action, event, )) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 60833a10..f8ae3b12 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -596,12 +596,13 @@ class QemuVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ + if self.started: - log.info("Process Qemu is dead. Return code: %d", returncode) + log.info("QEMU process has stopped, return code: %d", returncode) self.status = "stopped" self._process = None diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index dee7cdf9..a25d4c84 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -245,12 +245,12 @@ class VPCSVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ if self._started: - log.info("Process VPCS is dead. Return code: %d", returncode) + log.info("VPCS process has stopped, return code: %d", returncode) self._started = False self.status = "stopped" self._process = None diff --git a/gns3server/utils/asyncio.py b/gns3server/utils/asyncio.py index 7c9cc606..39e1ac8e 100644 --- a/gns3server/utils/asyncio.py +++ b/gns3server/utils/asyncio.py @@ -17,7 +17,6 @@ import asyncio -import shutil import sys @@ -91,6 +90,6 @@ def _check_process(process, termination_callback): def monitor_process(process, termination_callback): - """Call termination_callback when process die""" + """Call termination_callback when a process dies""" asyncio.async(_check_process(process, termination_callback)) From a386c5382ca6d7d26b067be89209be6e982e9de3 Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 14 May 2015 20:11:57 -0600 Subject: [PATCH 013/336] Starting VMware VM networking support. --- gns3server/modules/vmware/__init__.py | 8 +- gns3server/modules/vmware/vmware_vm.py | 128 ++++++++++++++++++++++++- gns3server/schemas/vmware.py | 33 +++++++ 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 49e7bbee..e02f20b2 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -113,7 +113,7 @@ class VMware(BaseManager): return stdout_data.decode("utf-8", errors="ignore").splitlines() @staticmethod - def _parse_vmware_file(path): + def parse_vmware_file(path): """ Parses a VMware file (VMX, preferences or inventory). @@ -145,7 +145,7 @@ class VMware(BaseManager): vms = [] try: log.debug('Reading VMware inventory file "{}"'.format(inventory_path)) - pairs = self._parse_vmware_file(inventory_path) + pairs = self.parse_vmware_file(inventory_path) for key, value in pairs.items(): if key.startswith("vmlist"): try: @@ -180,7 +180,7 @@ class VMware(BaseManager): vmx_path = os.path.join(path, filename) log.debug('Reading VMware VMX file "{}"'.format(vmx_path)) try: - pairs = self._parse_vmware_file(vmx_path) + pairs = self.parse_vmware_file(vmx_path) if "displayName" in pairs: log.debug('Found VM named "{}"'.format(pairs["displayName"])) vms.append({"vmname": pairs["displayName"], "vmx_path": vmx_path}) @@ -218,7 +218,7 @@ class VMware(BaseManager): if os.path.exists(vmware_preferences_path): # the default vm path may be present in VMware preferences file. try: - pairs = self._parse_vmware_file(vmware_preferences_path) + pairs = self.parse_vmware_file(vmware_preferences_path) if "prefvmx.defaultvmpath" in pairs: default_vm_path = pairs["prefvmx.defaultvmpath"] except OSError as e: diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 89a0554c..5918e590 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -28,14 +28,23 @@ import json import socket import asyncio +from gns3server.utils.interfaces import interfaces from pkg_resources import parse_version from .vmware_error import VMwareError from ..nios.nio_udp import NIOUDP +from ..adapters.ethernet_adapter import EthernetAdapter from ..base_vm import BaseVM import logging log = logging.getLogger(__name__) +VMX_ETHERNET_TEMPLATE = """ +ethernet{number}.present = "TRUE" +ethernet{number}.connectionType = "hostonly" +ethernet{number}.addressType = "generated" +ethernet{number}.generatedAddressOffset = "0" +ethernet{number}.autoDetect = "TRUE" +""" class VMwareVM(BaseVM): @@ -53,6 +62,13 @@ class VMwareVM(BaseVM): # VMware VM settings self._headless = False self._vmx_path = vmx_path + self._enable_remote_console = False + self._adapters = 0 + self._ethernet_adapters = {} + self._adapter_type = "e1000" + + if not os.path.exists(vmx_path): + raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{}"'.format(name, vmx_path)) def __json__(self): @@ -61,7 +77,10 @@ class VMwareVM(BaseVM): "console": self.console, "project_id": self.project.id, "vmx_path": self.vmx_path, - "headless": self.headless} + "headless": self.headless, + "enable_remote_console": self.enable_remote_console, + "adapters": self._adapters, + "adapter_type": self.adapter_type} @asyncio.coroutine def _control_vm(self, subcommand, *additional_args): @@ -72,12 +91,39 @@ class VMwareVM(BaseVM): log.debug("Control VM '{}' result: {}".format(subcommand, result)) return result + def _set_network_options(self): + + try: + self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path) + except OSError as e: + raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) + + vmnet_interfaces = interfaces() + print(vmnet_interfaces) + + for adapter_number in range(0, self._adapters): + nio = self._ethernet_adapters[adapter_number].get_nio(0) + if nio and isinstance(nio, NIOUDP): + connection_type = "ethernet{}.connectionType".format(adapter_number) + if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] not in ("hostonly", "custom"): + raise VMwareError("Attachment ({}) already configured on adapter {}. " + "Please set it to 'hostonly' or 'custom to allow GNS3 to use it.".format(self._vmx_pairs[connection_type], + adapter_number)) + + vnet = "ethernet{}.vnet".format(adapter_number) + if vnet in self._vmx_pairs: + pass + #if "ethernet{}.present".format(adapter_number) in self._vmx_pairs: + # print("ETHERNET {} FOUND".format(adapter_number)) + #ethernet0.vnet + @asyncio.coroutine def start(self): """ Starts this VMware VM. """ + self._set_network_options() if self._headless: yield from self._control_vm("start", "nogui") else: @@ -193,3 +239,83 @@ class VMwareVM(BaseVM): log.info("VMware VM '{name}' [{id}] has set the vmx file path to '{vmx}'".format(name=self.name, id=self.id, vmx=vmx_path)) self._vmx_path = vmx_path + + @property + def enable_remote_console(self): + """ + Returns either the remote console is enabled or not + + :returns: boolean + """ + + return self._enable_remote_console + + @asyncio.coroutine + def set_enable_remote_console(self, enable_remote_console): + """ + Sets either the console is enabled or not + + :param enable_remote_console: boolean + """ + + if enable_remote_console: + log.info("VMware VM '{name}' [{id}] has enabled the console".format(name=self.name, id=self.id)) + #self._start_remote_console() + else: + log.info("VMware VM '{name}' [{id}] has disabled the console".format(name=self.name, id=self.id)) + #self._stop_remote_console() + self._enable_remote_console = enable_remote_console + + @property + def adapters(self): + """ + Returns the number of adapters configured for this VMware VM. + + :returns: number of adapters + """ + + return self._adapters + + @adapters.setter + def adapters(self, adapters): + """ + Sets the number of Ethernet adapters for this VMware VM instance. + + :param adapters: number of adapters + """ + + # VMware VMs are limit to 10 adapters + if adapters > 10: + raise VMwareError("Number of adapters above the maximum supported of 10") + + self._ethernet_adapters.clear() + for adapter_number in range(0, adapters): + self._ethernet_adapters[adapter_number] = EthernetAdapter() + + self._adapters = len(self._ethernet_adapters) + log.info("VMware VM '{name}' [{id}] has changed the number of Ethernet adapters to {adapters}".format(name=self.name, + id=self.id, + adapters=adapters)) + + @property + def adapter_type(self): + """ + Returns the adapter type for this VMware VM instance. + + :returns: adapter type (string) + """ + + return self._adapter_type + + @adapter_type.setter + def adapter_type(self, adapter_type): + """ + Sets the adapter type for this VMware VM instance. + + :param adapter_type: adapter type (string) + """ + + self._adapter_type = adapter_type + log.info("VMware VM '{name}' [{id}]: adapter type changed to {adapter_type}".format(name=self.name, + id=self.id, + adapter_type=adapter_type)) diff --git a/gns3server/schemas/vmware.py b/gns3server/schemas/vmware.py index 959bef5b..28fb78a1 100644 --- a/gns3server/schemas/vmware.py +++ b/gns3server/schemas/vmware.py @@ -56,6 +56,17 @@ VMWARE_CREATE_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "adapters": { + "description": "number of adapters", + "type": "integer", + "minimum": 0, + "maximum": 10, # maximum adapters support by VMware VMs + }, + "adapter_type": { + "description": "VMware adapter type", + "type": "string", + "minLength": 1, + }, }, "additionalProperties": False, "required": ["name", "vmx_path", "linked_clone"], @@ -90,6 +101,17 @@ VMWARE_UPDATE_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "adapters": { + "description": "number of adapters", + "type": "integer", + "minimum": 0, + "maximum": 10, # maximum adapters support by VMware VMs + }, + "adapter_type": { + "description": "VMware adapter type", + "type": "string", + "minLength": 1, + }, }, "additionalProperties": False, } @@ -131,6 +153,17 @@ VMWARE_OBJECT_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "adapters": { + "description": "number of adapters", + "type": "integer", + "minimum": 0, + "maximum": 10, # maximum adapters support by VMware VMs + }, + "adapter_type": { + "description": "VMware adapter type", + "type": "string", + "minLength": 1, + }, "console": { "description": "console TCP port", "minimum": 1, From cb7b9e8190253f0b49a2091d8bb29e31c9660480 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 15 May 2015 19:09:48 -0600 Subject: [PATCH 014/336] Adapters for VMware VMs. --- gns3server/handlers/api/vmware_handler.py | 52 ++++++++++++++++++++++ gns3server/modules/vmware/__init__.py | 23 ++++++++-- gns3server/modules/vmware/vmware_vm.py | 54 ++++++++++++++++++++++- gns3server/utils/interfaces.py | 3 +- 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/gns3server/handlers/api/vmware_handler.py b/gns3server/handlers/api/vmware_handler.py index 65b39316..c574b070 100644 --- a/gns3server/handlers/api/vmware_handler.py +++ b/gns3server/handlers/api/vmware_handler.py @@ -15,10 +15,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +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.nio import NIO_SCHEMA from ...modules.vmware import VMware from ...modules.project_manager import ProjectManager @@ -240,3 +242,53 @@ class VMwareHandler: vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) yield from vm.reload() response.set_status(204) + + @Route.post( + r"/projects/{project_id}/vmware/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance", + "adapter_number": "Adapter where the nio should be added", + "port_number": "Port on the adapter (always 0)" + }, + status_codes={ + 201: "NIO created", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Add a NIO to a VMware VM instance", + input=NIO_SCHEMA, + output=NIO_SCHEMA) + def create_nio(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + nio_type = request.json["type"] + if nio_type != "nio_udp": + raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type)) + nio = vmware_manager.create_nio(None, request.json) + vm.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}/vmware/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance", + "adapter_number": "Adapter from where the nio should be removed", + "port_number": "Port on the adapter (always 0)" + }, + status_codes={ + 204: "NIO deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Remove a NIO from a VMware VM instance") + def delete_nio(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"])) + response.set_status(204) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index e02f20b2..e0a5fe44 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -81,6 +81,23 @@ class VMware(BaseManager): self._vmrun_path = vmrun_path return vmrun_path + @property + def host_type(self): + """ + Returns the VMware host type. + player = VMware player + ws = VMware Workstation + fusion = VMware Fusion + + :returns: host type (string) + """ + + if sys.platform.startswith("darwin"): + host_type = "fusion" + else: + host_type = self.config.get_section_config("VMware").get("host_type", "ws") + return host_type + @asyncio.coroutine def execute(self, subcommand, args, timeout=60, host_type=None): @@ -88,10 +105,8 @@ class VMware(BaseManager): if not vmrun_path: vmrun_path = self.find_vmrun() if host_type is None: - if sys.platform.startswith("darwin"): - host_type = "fusion" - else: - host_type = self.config.get_section_config("VMware").get("host_type", "ws") + host_type = self.host_type + command = [vmrun_path, "-T", host_type, subcommand] command.extend(args) log.debug("Executing vmrun with command: {}".format(command)) diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 5918e590..b84bb546 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -145,6 +145,8 @@ class VMwareVM(BaseVM): Suspends this VMware VM. """ + if self.manager.host_type != "ws": + raise VMwareError("Pausing a VM is only supported by VMware Workstation") yield from self._control_vm("pause") log.info("VMware VM '{name}' [{id}] paused".format(name=self.name, id=self.id)) @@ -154,6 +156,8 @@ class VMwareVM(BaseVM): Resumes this VMware VM. """ + if self.manager.host_type != "ws": + raise VMwareError("Unpausing a VM is only supported by VMware Workstation") yield from self._control_vm("unpause") log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id)) @@ -250,8 +254,8 @@ class VMwareVM(BaseVM): return self._enable_remote_console - @asyncio.coroutine - def set_enable_remote_console(self, enable_remote_console): + @enable_remote_console.setter + def enable_remote_console(self, enable_remote_console): """ Sets either the console is enabled or not @@ -319,3 +323,49 @@ class VMwareVM(BaseVM): log.info("VMware VM '{name}' [{id}]: adapter type changed to {adapter_type}".format(name=self.name, id=self.id, adapter_type=adapter_type)) + + def adapter_add_nio_binding(self, adapter_number, nio): + """ + Adds an adapter NIO binding. + + :param adapter_number: adapter number + :param nio: NIO instance to add to the slot/port + """ + + try: + adapter = self._ethernet_adapters[adapter_number] + except IndexError: + raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name, + adapter_number=adapter_number)) + + adapter.add_nio(0, nio) + log.info("VMware VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name, + id=self.id, + nio=nio, + adapter_number=adapter_number)) + + def adapter_remove_nio_binding(self, adapter_number): + """ + Removes an adapter NIO binding. + + :param adapter_number: adapter number + + :returns: NIO instance + """ + + try: + adapter = self._ethernet_adapters[adapter_number] + except IndexError: + raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name, + adapter_number=adapter_number)) + + nio = adapter.get_nio(0) + if isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) + adapter.remove_nio(0) + + log.info("VMware VM '{name}' [{id}]: {nio} removed from adapter {adapter_number}".format(name=self.name, + id=self.id, + nio=nio, + adapter_number=adapter_number)) + return nio diff --git a/gns3server/utils/interfaces.py b/gns3server/utils/interfaces.py index 4123459d..c9a5df88 100644 --- a/gns3server/utils/interfaces.py +++ b/gns3server/utils/interfaces.py @@ -69,7 +69,8 @@ def get_windows_interfaces(): # adapter is connected or media disconnected npf_interface = "\\Device\\NPF_{guid}".format(guid=adapter.GUID) interfaces.append({"id": npf_interface, - "name": adapter.NetConnectionID}) + "name": adapter.NetConnectionID, + "netcard": adapter.name}) except (AttributeError, pywintypes.com_error): log.warn("Could not use the COM service to retrieve interface info, trying using the registry...") return _get_windows_interfaces_from_registry() From 0287b4607d07018f0d9b9b1a2105c11e96e0f557 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 20 May 2015 19:05:26 -0600 Subject: [PATCH 015/336] Base for supporting VMnet adapters. --- gns3server/modules/vmware/__init__.py | 24 ++- gns3server/modules/vmware/vmware_vm.py | 253 ++++++++++++++++++++++--- 2 files changed, 247 insertions(+), 30 deletions(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index e0a5fe44..1a980e6b 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -25,6 +25,7 @@ import shutil import asyncio import subprocess import logging +from collections import OrderedDict log = logging.getLogger(__name__) @@ -137,7 +138,7 @@ class VMware(BaseManager): :returns: dict """ - pairs = {} + pairs = OrderedDict() with open(path, encoding="utf-8") as f: for line in f.read().splitlines(): try: @@ -147,6 +148,25 @@ class VMware(BaseManager): continue return pairs + @staticmethod + def write_vmx_file(path, pairs): + """ + Write a VMware VMX file. + + :param path: path to the VMX file + :param pairs: settings to write + """ + + with open(path, "w", encoding="utf-8") as f: + if sys.platform.startswith("linux"): + # write the shebang on the first line on Linux + vmware_path = shutil.which("vmware") + if vmware_path: + f.write("#!{}\n".format(vmware_path)) + for key, value in pairs.items(): + entry = '{} = "{}"\n'.format(key, value) + f.write(entry) + def _get_vms_from_inventory(self, inventory_path): """ Searches for VMs by parsing a VMware inventory file. @@ -175,7 +195,7 @@ class VMware(BaseManager): for vm_settings in vm_entries.values(): if "DisplayName" in vm_settings and "config" in vm_settings: - log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayName"], vm_settings["config"])) + log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["DisplayName"], vm_settings["config"])) vms.append({"vmname": vm_settings["DisplayName"], "vmx_path": vm_settings["config"]}) return vms diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index b84bb546..353fd167 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -20,31 +20,30 @@ VMware VM instance. """ import sys -import shlex -import re import os import tempfile import json import socket +import re +import subprocess +import configparser +import shutil import asyncio from gns3server.utils.interfaces import interfaces +from gns3server.utils.asyncio import wait_for_process_termination +from gns3server.utils.asyncio import monitor_process from pkg_resources import parse_version from .vmware_error import VMwareError from ..nios.nio_udp import NIOUDP from ..adapters.ethernet_adapter import EthernetAdapter from ..base_vm import BaseVM + import logging log = logging.getLogger(__name__) -VMX_ETHERNET_TEMPLATE = """ -ethernet{number}.present = "TRUE" -ethernet{number}.connectionType = "hostonly" -ethernet{number}.addressType = "generated" -ethernet{number}.generatedAddressOffset = "0" -ethernet{number}.autoDetect = "TRUE" -""" + class VMwareVM(BaseVM): @@ -57,6 +56,8 @@ class VMwareVM(BaseVM): super().__init__(name, vm_id, project, manager, console=console) self._linked_clone = linked_clone + self._ubridge_process = None + self._ubridge_stdout_file = "" self._closed = False # VMware VM settings @@ -91,6 +92,26 @@ class VMwareVM(BaseVM): log.debug("Control VM '{}' result: {}".format(subcommand, result)) return result + def _get_vmnet_interfaces(self): + + vmnet_intefaces = [] + for interface in interfaces(): + if sys.platform.startswith("win"): + if "netcard" in interface: + windows_name = interface["netcard"] + else: + windows_name = interface["name"] + match = re.search("(VMnet[0-9]+)", windows_name) + if match: + vmnet = match.group(1) + if vmnet not in ("VMnet1", "VMnet8"): + vmnet_intefaces.append(vmnet) + elif interface["name"].startswith("vmnet"): + vmnet = interface["name"] + if vmnet not in ("vmnet1", "vmnet8"): + vmnet_intefaces.append(interface["name"]) + return vmnet_intefaces + def _set_network_options(self): try: @@ -98,24 +119,184 @@ class VMwareVM(BaseVM): except OSError as e: raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) - vmnet_interfaces = interfaces() - print(vmnet_interfaces) - + vmnet_interfaces = self._get_vmnet_interfaces() + for adapter_number in range(0, self._adapters): + nio = self._ethernet_adapters[adapter_number].get_nio(0) + if nio: + if "ethernet{}.present".format(adapter_number) in self._vmx_pairs: + + # check for the connection type + connection_type = "ethernet{}.connectionType".format(adapter_number) + if connection_type in self._vmx_pairs: + if self._vmx_pairs[connection_type] not in ("hostonly", "custom"): + raise VMwareError("Attachment ({}) already configured on adapter {}. " + "Please set it to 'hostonly' or 'custom' to allow GNS3 to use it.".format(self._vmx_pairs[connection_type], + adapter_number)) + # check for the vmnet interface + vnet = "ethernet{}.vnet".format(adapter_number) + if vnet in self._vmx_pairs: + vmnet = os.path.basename(self._vmx_pairs[vnet]) + if vmnet in vmnet_interfaces: + vmnet_interfaces.remove(vmnet) + else: + raise VMwareError("Network adapter {} is not associated with a VMnet interface".format(adapter_number)) + + # check for adapter type + # adapter_type = "ethernet{}.virtualDev".format(adapter_number) + # if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type: + # raise VMwareError("Network adapter {} is not of type {}".format(self._adapter_type)) + # else: + # self._vmx_pairs[adapter_type] = self._adapter_type + else: + new_ethernet_adapter = {"ethernet{}.present".format(adapter_number): "TRUE", + "ethernet{}.connectionType".format(adapter_number): "custom", + "ethernet{}.vnet".format(adapter_number): "vmnet1", + "ethernet{}.addressType".format(adapter_number): "generated", + "ethernet{}.generatedAddressOffset".format(adapter_number): "0"} + self._vmx_pairs.update(new_ethernet_adapter) + + #raise VMwareError("Network adapter {} does not exist".format(adapter_number)) + + self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) + self._update_ubridge_config() + + def _update_ubridge_config(self): + """ + Updates the ubrige.ini file. + """ + + ubridge_ini = os.path.join(self.working_dir, "ubridge.ini") + config = configparser.ConfigParser() for adapter_number in range(0, self._adapters): nio = self._ethernet_adapters[adapter_number].get_nio(0) - if nio and isinstance(nio, NIOUDP): - connection_type = "ethernet{}.connectionType".format(adapter_number) - if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] not in ("hostonly", "custom"): - raise VMwareError("Attachment ({}) already configured on adapter {}. " - "Please set it to 'hostonly' or 'custom to allow GNS3 to use it.".format(self._vmx_pairs[connection_type], - adapter_number)) + if nio: + bridge_name = "bridge{}".format(adapter_number) vnet = "ethernet{}.vnet".format(adapter_number) - if vnet in self._vmx_pairs: - pass - #if "ethernet{}.present".format(adapter_number) in self._vmx_pairs: - # print("ETHERNET {} FOUND".format(adapter_number)) - #ethernet0.vnet + if not vnet in self._vmx_pairs: + continue + + vmnet_interface = os.path.basename(self._vmx_pairs[vnet]) + if sys.platform.startswith("linux"): + config[bridge_name] = {"source_linux_raw": vmnet_interface} + elif sys.platform.startswith("win"): + windows_interfaces = interfaces() + npf = None + for interface in windows_interfaces: + if "netcard" in interface and vmnet_interface in interface["netcard"]: + npf = interface["id"] + elif vmnet_interface in interface["name"]: + npf = interface["id"] + if npf: + config[bridge_name] = {"source_ethernet": npf} + else: + raise VMwareError("Could not find NPF id for VMnet interface {}".format(vmnet_interface)) + else: + config[bridge_name] = {"source_ethernet": vmnet_interface} + + if isinstance(nio, NIOUDP): + udp_tunnel_info = {"destination_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport, + rhost=nio.rhost, + rport=nio.rport)} + config[bridge_name].update(udp_tunnel_info) + + if nio.capturing: + capture_info = {"pcap_file": "{pcap_file}".format(pcap_file=nio.pcap_output_file)} + config[bridge_name].update(capture_info) + + try: + with open(ubridge_ini, "w", encoding="utf-8") as config_file: + config.write(config_file) + log.info('VMware VM "{name}" [id={id}]: ubridge.ini updated'.format(name=self._name, + id=self._id)) + except OSError as e: + raise VMwareError("Could not create {}: {}".format(ubridge_ini, e)) + + @property + def ubridge_path(self): + """ + Returns the uBridge executable path. + + :returns: path to uBridge + """ + + path = self._manager.config.get_section_config("VMware").get("ubridge_path", "ubridge") + if path == "ubridge": + path = shutil.which("ubridge") + return path + + @asyncio.coroutine + def _start_ubridge(self): + """ + Starts uBridge (handles connections to and from this VMware VM). + """ + + try: + #self._update_ubridge_config() + command = [self.ubridge_path] + log.info("starting ubridge: {}".format(command)) + self._ubridge_stdout_file = os.path.join(self.working_dir, "ubridge.log") + log.info("logging to {}".format(self._ubridge_stdout_file)) + with open(self._ubridge_stdout_file, "w", encoding="utf-8") as fd: + self._ubridge_process = yield from asyncio.create_subprocess_exec(*command, + stdout=fd, + stderr=subprocess.STDOUT, + cwd=self.working_dir) + + monitor_process(self._ubridge_process, self._termination_callback) + log.info("ubridge started PID={}".format(self._ubridge_process.pid)) + except (OSError, subprocess.SubprocessError) as e: + ubridge_stdout = self.read_ubridge_stdout() + log.error("Could not start ubridge: {}\n{}".format(e, ubridge_stdout)) + raise VMwareError("Could not start ubridge: {}\n{}".format(e, ubridge_stdout)) + + def _termination_callback(self, returncode): + """ + Called when the process has stopped. + + :param returncode: Process returncode + """ + + log.info("uBridge process has stopped, return code: %d", returncode) + + def is_ubridge_running(self): + """ + Checks if the ubridge process is running + + :returns: True or False + """ + + if self._ubridge_process and self._ubridge_process.returncode is None: + return True + return False + + def read_ubridge_stdout(self): + """ + Reads the standard output of the uBridge process. + Only use when the process has been stopped or has crashed. + """ + + output = "" + if self._ubridge_stdout_file: + try: + with open(self._ubridge_stdout_file, "rb") as file: + output = file.read().decode("utf-8", errors="replace") + except OSError as e: + log.warn("could not read {}: {}".format(self._ubridge_stdout_file, e)) + return output + + def _terminate_process_ubridge(self): + """ + Terminate the ubridge process if running. + """ + + if self._ubridge_process: + log.info('Stopping uBridge process for VMware VM "{}" PID={}'.format(self.name, self._ubridge_process.pid)) + try: + self._ubridge_process.terminate() + # Sometime the process can already be dead when we garbage collect + except ProcessLookupError: + pass @asyncio.coroutine def start(self): @@ -123,7 +304,13 @@ class VMwareVM(BaseVM): Starts this VMware VM. """ + ubridge_path = self.ubridge_path + if not ubridge_path or not os.path.isfile(ubridge_path): + raise VMwareError("ubridge is necessary to start a VMware VM") + self._set_network_options() + yield from self._start_ubridge() + if self._headless: yield from self._control_vm("start", "nogui") else: @@ -136,6 +323,16 @@ class VMwareVM(BaseVM): Stops this VMware VM. """ + if self.is_ubridge_running(): + self._terminate_process_ubridge() + try: + yield from wait_for_process_termination(self._ubridge_process, timeout=3) + except asyncio.TimeoutError: + if self._ubridge_process.returncode is None: + log.warn("uBridge process {} is still running... killing it".format(self._ubridge_process.pid)) + self._ubridge_process.kill() + self._ubridge_process = None + yield from self._control_vm("stop") log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) @@ -185,11 +382,11 @@ class VMwareVM(BaseVM): self._manager.port_manager.release_tcp_port(self._console, self._project) self._console = None - #for adapter in self._ethernet_adapters.values(): - # if adapter is not None: - # for nio in adapter.ports.values(): - # if nio and isinstance(nio, NIOUDP): - # self.manager.port_manager.release_udp_port(nio.lport, self._project) + for adapter in self._ethernet_adapters.values(): + if adapter is not None: + for nio in adapter.ports.values(): + if nio and isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) try: yield from self.stop() From 3729a1078392712fa28a945972c467dcfc4048e9 Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 21 May 2015 21:48:59 -0600 Subject: [PATCH 016/336] VMware vmnets management almost complete. --- .../modules/virtualbox/virtualbox_vm.py | 4 +- gns3server/modules/vmware/__init__.py | 65 ++++++ gns3server/modules/vmware/vmware_vm.py | 191 +++++++++++++----- gns3server/schemas/vmware.py | 12 ++ 4 files changed, 216 insertions(+), 56 deletions(-) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 320ed9c7..4ec0aa37 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -504,7 +504,7 @@ class VirtualBoxVM(BaseVM): """ Returns either GNS3 can use any VirtualBox adapter on this instance. - :returns: index + :returns: boolean """ return self._use_any_adapter @@ -520,7 +520,7 @@ class VirtualBoxVM(BaseVM): if use_any_adapter: log.info("VirtualBox VM '{name}' [{id}] is allowed to use any adapter".format(name=self.name, id=self.id)) else: - log.info("VirtualBox VM '{name}' [{id}] is not allowd to use any adapter".format(name=self.name, id=self.id)) + log.info("VirtualBox VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id)) self._use_any_adapter = use_any_adapter @property diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 1a980e6b..780b7597 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -21,11 +21,14 @@ VMware player/workstation server module. import os import sys +import re import shutil import asyncio import subprocess import logging + from collections import OrderedDict +from gns3server.utils.interfaces import interfaces log = logging.getLogger(__name__) @@ -42,6 +45,12 @@ class VMware(BaseManager): super().__init__() self._vmrun_path = None + self._vmnets = [] + self._vmnet_start_range = 2 + if sys.platform.startswith("win"): + self._vmnet_end_range = 19 + else: + self._vmnet_end_range = 255 @property def vmrun_path(self): @@ -82,6 +91,62 @@ class VMware(BaseManager): self._vmrun_path = vmrun_path return vmrun_path + @staticmethod + def get_vmnet_interfaces(): + + vmnet_interfaces = [] + for interface in interfaces(): + if sys.platform.startswith("win"): + if "netcard" in interface: + windows_name = interface["netcard"] + else: + windows_name = interface["name"] + match = re.search("(VMnet[0-9]+)", windows_name) + if match: + vmnet = match.group(1) + if vmnet not in ("VMnet1", "VMnet8"): + vmnet_interfaces.append(vmnet) + elif interface["name"].startswith("vmnet"): + vmnet = interface["name"] + if vmnet not in ("vmnet1", "vmnet8"): + vmnet_interfaces.append(interface["name"]) + return vmnet_interfaces + + def is_managed_vmnet(self, vmnet): + + self._vmnet_start_range = self.config.get_section_config("VMware").getint("vmnet_start_range", self._vmnet_start_range) + self._vmnet_end_range = self.config.get_section_config("VMware").getint("vmnet_end_range", self._vmnet_end_range) + match = re.search("vmnet([0-9]+)$", vmnet, re.IGNORECASE) + if match: + vmnet_number = match.group(1) + if self._vmnet_start_range <= int(vmnet_number) <= self._vmnet_end_range: + return True + return False + + def allocate_vmnet(self): + + if not self._vmnets: + raise VMwareError("No more VMnet interfaces available") + return self._vmnets.pop(0) + + def refresh_vmnet_list(self): + + vmnet_interfaces = self.get_vmnet_interfaces() + + # remove vmnets already in use + for vm in self._vms.values(): + for used_vmnet in vm.vmnets: + if used_vmnet in vmnet_interfaces: + log.debug("{} is already in use".format(used_vmnet)) + vmnet_interfaces.remove(used_vmnet) + + # remove vmnets that are not managed + for vmnet in vmnet_interfaces.copy(): + if vmnet in vmnet_interfaces and self.is_managed_vmnet(vmnet) is False: + vmnet_interfaces.remove(vmnet) + + self._vmnets = vmnet_interfaces + @property def host_type(self): """ diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 353fd167..2df012f0 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -30,9 +30,9 @@ import configparser import shutil import asyncio -from gns3server.utils.interfaces import interfaces from gns3server.utils.asyncio import wait_for_process_termination from gns3server.utils.asyncio import monitor_process +from collections import OrderedDict from pkg_resources import parse_version from .vmware_error import VMwareError from ..nios.nio_udp import NIOUDP @@ -56,8 +56,11 @@ class VMwareVM(BaseVM): super().__init__(name, vm_id, project, manager, console=console) self._linked_clone = linked_clone + self._vmx_pairs = OrderedDict() self._ubridge_process = None self._ubridge_stdout_file = "" + self._vmnets = [] + self._maximum_adapters = 10 self._closed = False # VMware VM settings @@ -67,6 +70,7 @@ class VMwareVM(BaseVM): self._adapters = 0 self._ethernet_adapters = {} self._adapter_type = "e1000" + self._use_any_adapter = False if not os.path.exists(vmx_path): raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{}"'.format(name, vmx_path)) @@ -81,7 +85,13 @@ class VMwareVM(BaseVM): "headless": self.headless, "enable_remote_console": self.enable_remote_console, "adapters": self._adapters, - "adapter_type": self.adapter_type} + "adapter_type": self.adapter_type, + "use_any_adapter": self.use_any_adapter} + + @property + def vmnets(self): + + return self._vmnets @asyncio.coroutine def _control_vm(self, subcommand, *additional_args): @@ -92,25 +102,15 @@ class VMwareVM(BaseVM): log.debug("Control VM '{}' result: {}".format(subcommand, result)) return result - def _get_vmnet_interfaces(self): + def _get_vmx_setting(self, name, value=None): - vmnet_intefaces = [] - for interface in interfaces(): - if sys.platform.startswith("win"): - if "netcard" in interface: - windows_name = interface["netcard"] - else: - windows_name = interface["name"] - match = re.search("(VMnet[0-9]+)", windows_name) - if match: - vmnet = match.group(1) - if vmnet not in ("VMnet1", "VMnet8"): - vmnet_intefaces.append(vmnet) - elif interface["name"].startswith("vmnet"): - vmnet = interface["name"] - if vmnet not in ("vmnet1", "vmnet8"): - vmnet_intefaces.append(interface["name"]) - return vmnet_intefaces + if name in self._vmx_pairs: + if value is not None: + if self._vmx_pairs[name] == value: + return value + else: + return self._vmx_pairs[name] + return None def _set_network_options(self): @@ -119,43 +119,70 @@ class VMwareVM(BaseVM): except OSError as e: raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) - vmnet_interfaces = self._get_vmnet_interfaces() + # first to some sanity checks for adapter_number in range(0, self._adapters): - nio = self._ethernet_adapters[adapter_number].get_nio(0) - if nio: - if "ethernet{}.present".format(adapter_number) in self._vmx_pairs: - - # check for the connection type - connection_type = "ethernet{}.connectionType".format(adapter_number) - if connection_type in self._vmx_pairs: - if self._vmx_pairs[connection_type] not in ("hostonly", "custom"): - raise VMwareError("Attachment ({}) already configured on adapter {}. " - "Please set it to 'hostonly' or 'custom' to allow GNS3 to use it.".format(self._vmx_pairs[connection_type], - adapter_number)) - # check for the vmnet interface + connected = "ethernet{}.startConnected".format(adapter_number) + if self._get_vmx_setting(connected): + del self._vmx_pairs[connected] + + # check if any vmnet interface managed by GNS3 is being used on existing VMware adapters + if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"): + connection_type = "ethernet{}.connectionType".format(adapter_number) + if self._vmx_pairs[connection_type] in ("hostonly", "custom"): vnet = "ethernet{}.vnet".format(adapter_number) if vnet in self._vmx_pairs: vmnet = os.path.basename(self._vmx_pairs[vnet]) - if vmnet in vmnet_interfaces: - vmnet_interfaces.remove(vmnet) - else: - raise VMwareError("Network adapter {} is not associated with a VMnet interface".format(adapter_number)) - - # check for adapter type - # adapter_type = "ethernet{}.virtualDev".format(adapter_number) - # if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type: - # raise VMwareError("Network adapter {} is not of type {}".format(self._adapter_type)) - # else: - # self._vmx_pairs[adapter_type] = self._adapter_type - else: - new_ethernet_adapter = {"ethernet{}.present".format(adapter_number): "TRUE", - "ethernet{}.connectionType".format(adapter_number): "custom", - "ethernet{}.vnet".format(adapter_number): "vmnet1", - "ethernet{}.addressType".format(adapter_number): "generated", - "ethernet{}.generatedAddressOffset".format(adapter_number): "0"} - self._vmx_pairs.update(new_ethernet_adapter) + if self.manager.is_managed_vmnet(vmnet): + raise VMwareError("Network adapter {} is already associated with VMnet interface {} which is managed by GNS3, please remove".format(adapter_number, vmnet)) + + # check for adapter type + if self._adapter_type != "default": + adapter_type = "ethernet{}.virtualDev".format(adapter_number) + if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type: + raise VMwareError("Network adapter {} is not of type {}, please fix or remove it".format(self._adapter_type)) + + # check if connected to an adapter configured for nat or bridge + if self._ethernet_adapters[adapter_number].get_nio(0) and not self._use_any_adapter: + if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"): + # check for the connection type + connection_type = "ethernet{}.connectionType".format(adapter_number) + if connection_type in self._vmx_pairs: + if self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"): + raise VMwareError("Attachment ({}) already configured on network adapter {}. " + "Please remove it or allow GNS3 to use any adapter.".format(self._vmx_pairs[connection_type], + adapter_number)) - #raise VMwareError("Network adapter {} does not exist".format(adapter_number)) + # now configure VMware network adapters + self.manager.refresh_vmnet_list() + for adapter_number in range(0, self._adapters): + ethernet_adapter = {"ethernet{}.present".format(adapter_number): "TRUE", + "ethernet{}.addressType".format(adapter_number): "generated", + "ethernet{}.generatedAddressOffset".format(adapter_number): "0"} + self._vmx_pairs.update(ethernet_adapter) + if self._adapter_type != "default": + self._vmx_pairs["ethernet{}.virtualDev".format(adapter_number)] = self._adapter_type + + connection_type = "ethernet{}.connectionType".format(adapter_number) + if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"): + continue + + vnet = "ethernet{}.vnet".format(adapter_number) + if vnet in self._vmx_pairs: + vmnet = os.path.basename(self._vmx_pairs[vnet]) + else: + try: + vmnet = self.manager.allocate_vmnet() + finally: + self._vmnets.clear() + self._vmnets.append(vmnet) + self._vmx_pairs["ethernet{}.connectionType".format(adapter_number)] = "custom" + self._vmx_pairs["ethernet{}.vnet".format(adapter_number)] = vmnet + + # disable remaining network adapters + for adapter_number in range(self._adapters, self._maximum_adapters): + if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"): + log.debug("disabling remaining adapter {}".format(adapter_number)) + self._vmx_pairs["ethernet{}.startConnected".format(adapter_number)] = "FALSE" self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) self._update_ubridge_config() @@ -180,7 +207,7 @@ class VMwareVM(BaseVM): if sys.platform.startswith("linux"): config[bridge_name] = {"source_linux_raw": vmnet_interface} elif sys.platform.startswith("win"): - windows_interfaces = interfaces() + windows_interfaces = self.manager.get_vmnet_interfaces() npf = None for interface in windows_interfaces: if "netcard" in interface and vmnet_interface in interface["netcard"]: @@ -333,7 +360,39 @@ class VMwareVM(BaseVM): self._ubridge_process.kill() self._ubridge_process = None - yield from self._control_vm("stop") + try: + yield from self._control_vm("stop") + finally: + + self._vmnets.clear() + + try: + self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path) + except OSError as e: + raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) + + # remove the adapters managed by GNS3 + for adapter_number in range(0, self._adapters): + if self._get_vmx_setting("ethernet{}.vnet".format(adapter_number)) or \ + self._get_vmx_setting("ethernet{}.connectionType".format(adapter_number)) is None: + vnet = "ethernet{}.vnet".format(adapter_number) + if vnet in self._vmx_pairs: + vmnet = os.path.basename(self._vmx_pairs[vnet]) + if not self.manager.is_managed_vmnet(vmnet): + continue + log.debug("removing adapter {}".format(adapter_number)) + for key in self._vmx_pairs.keys(): + if key.startswith("ethernet{}.".format(adapter_number)): + del self._vmx_pairs[key] + + # re-enable any remaining network adapters + for adapter_number in range(self._adapters, self._maximum_adapters): + if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"): + log.debug("enabling remaining adapter {}".format(adapter_number)) + self._vmx_pairs["ethernet{}.startConnected".format(adapter_number)] = "TRUE" + + self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) + log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) @asyncio.coroutine @@ -521,6 +580,30 @@ class VMwareVM(BaseVM): id=self.id, adapter_type=adapter_type)) + @property + def use_any_adapter(self): + """ + Returns either GNS3 can use any VMware adapter on this instance. + + :returns: boolean + """ + + return self._use_any_adapter + + @use_any_adapter.setter + def use_any_adapter(self, use_any_adapter): + """ + Allows GNS3 to use any VMware adapter on this instance. + + :param use_any_adapter: boolean + """ + + if use_any_adapter: + log.info("VMware VM '{name}' [{id}] is allowed to use any adapter".format(name=self.name, id=self.id)) + else: + log.info("VMware VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id)) + self._use_any_adapter = use_any_adapter + def adapter_add_nio_binding(self, adapter_number, nio): """ Adds an adapter NIO binding. diff --git a/gns3server/schemas/vmware.py b/gns3server/schemas/vmware.py index 28fb78a1..a0465a3e 100644 --- a/gns3server/schemas/vmware.py +++ b/gns3server/schemas/vmware.py @@ -67,6 +67,10 @@ VMWARE_CREATE_SCHEMA = { "type": "string", "minLength": 1, }, + "use_any_adapter": { + "description": "allow GNS3 to use any VMware adapter", + "type": "boolean", + }, }, "additionalProperties": False, "required": ["name", "vmx_path", "linked_clone"], @@ -112,6 +116,10 @@ VMWARE_UPDATE_SCHEMA = { "type": "string", "minLength": 1, }, + "use_any_adapter": { + "description": "allow GNS3 to use any VMware adapter", + "type": "boolean", + }, }, "additionalProperties": False, } @@ -164,6 +172,10 @@ VMWARE_OBJECT_SCHEMA = { "type": "string", "minLength": 1, }, + "use_any_adapter": { + "description": "allow GNS3 to use any VMware adapter", + "type": "boolean", + }, "console": { "description": "console TCP port", "minimum": 1, From 3c4a60cd0ae526ca2dce3c3e5ddcd6f27eaf722b Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 25 May 2015 15:49:28 -0600 Subject: [PATCH 017/336] Check for VMware lock file. --- gns3server/modules/vmware/vmware_vm.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 2df012f0..44694476 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -21,10 +21,6 @@ VMware VM instance. import sys import os -import tempfile -import json -import socket -import re import subprocess import configparser import shutil @@ -33,7 +29,6 @@ import asyncio from gns3server.utils.asyncio import wait_for_process_termination from gns3server.utils.asyncio import monitor_process from collections import OrderedDict -from pkg_resources import parse_version from .vmware_error import VMwareError from ..nios.nio_udp import NIOUDP from ..adapters.ethernet_adapter import EthernetAdapter @@ -119,7 +114,7 @@ class VMwareVM(BaseVM): except OSError as e: raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) - # first to some sanity checks + # first do some sanity checks for adapter_number in range(0, self._adapters): connected = "ethernet{}.startConnected".format(adapter_number) if self._get_vmx_setting(connected): @@ -331,6 +326,9 @@ class VMwareVM(BaseVM): Starts this VMware VM. """ + if os.path.exists(self._vmx_path + ".lck"): + raise VMwareError("VM locked, it is either running or being edited in VMware") + ubridge_path = self.ubridge_path if not ubridge_path or not os.path.isfile(ubridge_path): raise VMwareError("ubridge is necessary to start a VMware VM") From 6f9f004ebb78e6684b96ba3d0a9253d94909be11 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 14 May 2015 12:03:17 +0200 Subject: [PATCH 018/336] API for list & download files of a project --- gns3server/handlers/api/project_handler.py | 73 +++++++++++++++++++++- gns3server/modules/project.py | 40 ++++++++++++ gns3server/schemas/project.py | 23 +++++++ tests/conftest.py | 7 ++- tests/handlers/api/test_project.py | 40 +++++++++++- tests/modules/test_project.py | 26 ++++++++ 6 files changed, 205 insertions(+), 4 deletions(-) diff --git a/gns3server/handlers/api/project_handler.py b/gns3server/handlers/api/project_handler.py index 54fe3283..a5018db3 100644 --- a/gns3server/handlers/api/project_handler.py +++ b/gns3server/handlers/api/project_handler.py @@ -15,13 +15,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import aiohttp import asyncio import json +import os from ...web.route import Route -from ...schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA, PROJECT_UPDATE_SCHEMA +from ...schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA, PROJECT_UPDATE_SCHEMA, PROJECT_FILE_LIST_SCHEMA from ...modules.project_manager import ProjectManager from ...modules import MODULES +from ...utils.asyncio import wait_run_in_executor import logging log = logging.getLogger() @@ -198,3 +201,71 @@ class ProjectHandler: response.write("{\"action\": \"ping\"}\n".encode("utf-8")) project.stop_listen_queue(queue) ProjectHandler._notifications_listening -= 1 + + @classmethod + @Route.get( + r"/projects/{project_id}/files", + description="List files of a project", + parameters={ + "project_id": "The UUID of the project", + }, + status_codes={ + 200: "Return list of files", + 404: "The project doesn't exist" + }, + output=PROJECT_FILE_LIST_SCHEMA) + def list_files(request, response): + + pm = ProjectManager.instance() + project = pm.get_project(request.match_info["project_id"]) + files = yield from project.list_files() + response.json(files) + response.set_status(200) + + @classmethod + @Route.get( + r"/projects/{project_id}/files/{path:.+}", + description="Get a file of a project", + parameters={ + "project_id": "The UUID of the project", + }, + status_codes={ + 200: "Return the file", + 403: "Permission denied", + 404: "The file doesn't exist" + }) + def get_file(request, response): + + pm = ProjectManager.instance() + project = pm.get_project(request.match_info["project_id"]) + path = request.match_info["path"] + path = os.path.normpath(path) + + # Raise error if user try to escape + if path[0] == ".": + raise aiohttp.web.HTTPForbidden + path = os.path.join(project.path, path) + + 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 + response.content_length = None + + try: + yield from wait_run_in_executor(ProjectHandler._read_file, path, request, response) + except FileNotFoundError: + raise aiohttp.web.HTTPNotFound() + except PermissionError: + raise aiohttp.web.HTTPForbidden + + @staticmethod + def _read_file(path, request, response): + + with open(path, "rb") as f: + response.start(request) + while True: + data = f.read(4096) + if not data: + break + response.write(data) diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index 85f4c7f5..fc191221 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -19,6 +19,7 @@ import aiohttp import os import shutil import asyncio +import hashlib from uuid import UUID, uuid4 from .port_manager import PortManager @@ -457,3 +458,42 @@ class Project: """Stop sending notification to this clients""" self._listeners.remove(queue) + + @asyncio.coroutine + def list_files(self): + """ + :returns: Array of files in project without temporary files. The files are dictionnary {"path": "test.bin", "md5sum": "aaaaa"} + """ + + files = [] + for (dirpath, dirnames, filenames) in os.walk(self.path): + for filename in filenames: + if not filename.endswith(".ghost"): + path = os.path.relpath(dirpath, self.path) + path = os.path.join(path, filename) + path = os.path.normpath(path) + file_info = {"path": path} + + try: + file_info["md5sum"] = yield from wait_run_in_executor(self._hash_file, os.path.join(dirpath, filename)) + except OSError: + continue + files.append(file_info) + + return files + + def _hash_file(self, path): + """ + Compute and md5 hash for file + + :returns: hexadecimal md5 + """ + + m = hashlib.md5() + with open(path, "rb") as f: + while True: + buf = f.read(128) + if not buf: + break + m.update(buf) + return m.hexdigest() diff --git a/gns3server/schemas/project.py b/gns3server/schemas/project.py index 3e9dfa6d..a07039b6 100644 --- a/gns3server/schemas/project.py +++ b/gns3server/schemas/project.py @@ -103,3 +103,26 @@ PROJECT_OBJECT_SCHEMA = { "additionalProperties": False, "required": ["location", "project_id", "temporary"] } + +PROJECT_FILE_LIST_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "List files in the project", + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "path": { + "description": "File path", + "type": ["string"] + }, + "md5sum": { + "description": "MD5 hash of the file", + "type": ["string"] + }, + + }, + } + ], + "additionalProperties": False, +} diff --git a/tests/conftest.py b/tests/conftest.py index 74aee0cb..5e49a7da 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,8 @@ import shutil import os import sys from aiohttp import web +from unittest.mock import patch + sys._called_from_test = True # Prevent execution of external binaries @@ -100,10 +102,11 @@ def server(request, loop, port_manager, monkeypatch): @pytest.fixture(scope="function") -def project(): +def project(tmpdir): """A GNS3 lab""" - return ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80") + p = ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80") + return p @pytest.fixture(scope="session") diff --git a/tests/handlers/api/test_project.py b/tests/handlers/api/test_project.py index 604ec756..432401b1 100644 --- a/tests/handlers/api/test_project.py +++ b/tests/handlers/api/test_project.py @@ -20,12 +20,14 @@ This test suite check /project endpoint """ import uuid +import os import asyncio import aiohttp from unittest.mock import patch from tests.utils import asyncio_patch from gns3server.handlers.api.project_handler import ProjectHandler +from gns3server.modules.project_manager import ProjectManager def test_create_project_with_path(server, tmpdir): @@ -175,6 +177,42 @@ def test_notification(server, project, loop): assert response.body == b'{"action": "ping"}\n{"action": "vm.created", "event": {"a": "b"}}\n' -def test_notification_invalid_id(server, project): +def test_notification_invalid_id(server): response = server.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4())) assert response.status == 404 + + +def test_list_files(server, project): + files = [ + { + "path": "test.txt", + "md5sum": "ad0234829205b9033196ba818f7a872b" + }, + { + "path": "vm-1/dynamips/test.bin", + "md5sum": "098f6bcd4621d373cade4e832627b4f6" + } + ] + with asyncio_patch("gns3server.modules.project.Project.list_files", return_value=files) as mock: + response = server.get("/projects/{project_id}/files".format(project_id=project.id), example=True) + assert response.status == 200 + assert response.json == files + + +def test_get_file(server, tmpdir): + + with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}): + project = ProjectManager.instance().create_project() + + with open(os.path.join(project.path, "hello"), "w+") as f: + f.write("world") + + response = server.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True, example=True) + assert response.status == 200 + assert response.body == b"world" + + response = server.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True) + assert response.status == 404 + + response = server.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) + assert response.status == 403 diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py index c4eaee4b..7e268d59 100644 --- a/tests/modules/test_project.py +++ b/tests/modules/test_project.py @@ -229,3 +229,29 @@ def test_clean_project_directory(tmpdir): assert os.path.exists(str(project1)) assert os.path.exists(str(oldproject)) assert not os.path.exists(str(project2)) + + +def test_list_files(tmpdir, loop): + + with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}): + project = Project() + path = project.path + os.makedirs(os.path.join(path, "vm-1", "dynamips")) + with open(os.path.join(path, "vm-1", "dynamips", "test.bin"), "w+") as f: + f.write("test") + open(os.path.join(path, "vm-1", "dynamips", "test.ghost"), "w+").close() + with open(os.path.join(path, "test.txt"), "w+") as f: + f.write("test2") + + files = loop.run_until_complete(asyncio.async(project.list_files())) + + assert files == [ + { + "path": "test.txt", + "md5sum": "ad0234829205b9033196ba818f7a872b" + }, + { + "path": os.path.join("vm-1", "dynamips", "test.bin"), + "md5sum": "098f6bcd4621d373cade4e832627b4f6" + } + ] From f169455ad471a0d3a8772229ec66195321a52b7f Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 14 May 2015 20:54:38 -0600 Subject: [PATCH 019/336] Adds NAT NIO in device schema validation so they can return an error that it is not supported. --- gns3server/schemas/dynamips_device.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gns3server/schemas/dynamips_device.py b/gns3server/schemas/dynamips_device.py index 9f173c49..201cd805 100644 --- a/gns3server/schemas/dynamips_device.py +++ b/gns3server/schemas/dynamips_device.py @@ -218,6 +218,16 @@ DEVICE_NIO_SCHEMA = { "required": ["type", "ethernet_device"], "additionalProperties": False }, + "NAT": { + "description": "NAT Network Input/Output", + "properties": { + "type": { + "enum": ["nio_nat"] + }, + }, + "required": ["type"], + "additionalProperties": False + }, "TAP": { "description": "TAP Network Input/Output", "properties": { @@ -291,6 +301,7 @@ DEVICE_NIO_SCHEMA = { {"$ref": "#/definitions/UDP"}, {"$ref": "#/definitions/Ethernet"}, {"$ref": "#/definitions/LinuxEthernet"}, + {"$ref": "#/definitions/NAT"}, {"$ref": "#/definitions/TAP"}, {"$ref": "#/definitions/UNIX"}, {"$ref": "#/definitions/VDE"}, From c5171b887f462db49670185f082682d3a2f582ec Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Sun, 17 May 2015 12:45:09 +0200 Subject: [PATCH 020/336] Add the fault handler in order to try to get a proper crash stack --- gns3server/crash_report.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 23ee7bc0..2cefe0e4 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -19,6 +19,10 @@ import os import sys import struct import platform +import faulthandler + +# Display a traceback in case of segfault crash. Usefull when frozen +faulthandler.enable() try: import raven From b62efa3baee9dcfef94072463ae1aee9094c18c7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Sun, 17 May 2015 23:10:50 +0200 Subject: [PATCH 021/336] I'm stupid... Remove fake segfault --- gns3server/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gns3server/main.py b/gns3server/main.py index f000c7ca..97eb064f 100644 --- a/gns3server/main.py +++ b/gns3server/main.py @@ -71,6 +71,5 @@ def main(): from gns3server.run import run run() - if __name__ == '__main__': main() From 29ec07d1668f1310126126d250f45fa21b786356 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 18 May 2015 11:58:56 +0200 Subject: [PATCH 022/336] Fix crash launching qemu on OSX from another location. It's append only when frozen an you launch the server by hand. Fix #194 --- gns3server/modules/qemu/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index 6dd532ba..70c1bf55 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -74,7 +74,11 @@ class Qemu(BaseManager): # add specific locations on Mac OS X regardless of what's in $PATH paths.extend(["/usr/local/bin", "/opt/local/bin"]) if hasattr(sys, "frozen"): - paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) + try: + paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) + # If the user run the server by hand from outside + except FileNotFoundError: + paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"]) for path in paths: try: for f in os.listdir(path): From 24e84a3dd87aeeedd3d6ccfa7c6139ec39d1b74b Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 21 May 2015 10:45:07 +0200 Subject: [PATCH 023/336] Test interfaces (it seem it's crash on Travis) --- tests/utils/test_interfaces.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/utils/test_interfaces.py diff --git a/tests/utils/test_interfaces.py b/tests/utils/test_interfaces.py new file mode 100644 index 00000000..9e899872 --- /dev/null +++ b/tests/utils/test_interfaces.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2014 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +from gns3server.utils.interfaces import interfaces + + +def test_interfaces(): + # This test should pass on all platforms without crash + assert isinstance(interfaces(), list) From d999f0a08d9a87a09c2bfd1b14eb3bd39c1d9808 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 21 May 2015 11:05:04 +0200 Subject: [PATCH 024/336] Drop coveralls because it's create trouble to tests run on Windows --- dev-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index af76ab92..45c6d0e3 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -6,4 +6,3 @@ pep8==1.5.7 pytest-timeout pytest-capturelog pytest-cov -python-coveralls From 1f931d56c7c9c97275afead63dd672298c8597b8 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 21 May 2015 11:46:55 +0200 Subject: [PATCH 025/336] Fix test suite on Windows --- tests/handlers/api/test_vpcs.py | 2 +- tests/modules/vpcs/test_vpcs_vm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/handlers/api/test_vpcs.py b/tests/handlers/api/test_vpcs.py index 5970306e..736fb953 100644 --- a/tests/handlers/api/test_vpcs.py +++ b/tests/handlers/api/test_vpcs.py @@ -52,7 +52,7 @@ def test_vpcs_create_startup_script(server, project): assert response.route == "/projects/{project_id}/vpcs/vms" assert response.json["name"] == "PC TEST 1" assert response.json["project_id"] == project.id - assert response.json["startup_script"] == "ip 192.168.1.2\necho TEST" + assert response.json["startup_script"] == os.linesep.join(["ip 192.168.1.2", "echo TEST"]) assert response.json["startup_script_path"] == "startup.vpc" diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index a33d4397..b4fe902a 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -200,7 +200,7 @@ def test_update_startup_script_h(vm): def test_get_startup_script(vm): - content = "echo GNS3 VPCS\nip 192.168.1.2\n" + content = os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) vm.startup_script = content assert vm.startup_script == content From e62dd1a5723b6e4c6995fe680754acec9a58e73c Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 21 May 2015 12:01:37 +0200 Subject: [PATCH 026/336] Fix tests on Windows --- tests/modules/vpcs/test_vpcs_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index b4fe902a..0d2b2266 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -202,7 +202,7 @@ def test_update_startup_script_h(vm): def test_get_startup_script(vm): content = os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) vm.startup_script = content - assert vm.startup_script == content + assert vm.startup_script == "echo GNS3 VPCS\nip 192.168.1.2" def test_get_startup_script_using_default_script(vm): From a7dd0d3c51807aec21cc9ed96d9878dbf6aa5123 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 25 May 2015 19:07:12 -0600 Subject: [PATCH 027/336] Fixes TAP connection when using VPCS. --- gns3server/modules/vpcs/vpcs_vm.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index a25d4c84..044e808f 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -406,6 +406,9 @@ class VPCSVM(BaseVM): command = [self.vpcs_path] command.extend(["-p", str(self._console)]) # listen to console port + command.extend(["-m", str(self._manager.get_mac_id(self.id))]) # the unique ID is used to set the MAC address offset + command.extend(["-i", "1"]) # option to start only one VPC instance + command.extend(["-F"]) # option to avoid the daemonization of VPCS nio = self._ethernet_adapter.get_nio(0) if nio: @@ -420,10 +423,6 @@ class VPCSVM(BaseVM): command.extend(["-e"]) command.extend(["-d", nio.tap_device]) - command.extend(["-m", str(self._manager.get_mac_id(self.id))]) # the unique ID is used to set the MAC address offset - command.extend(["-i", "1"]) # option to start only one VPC instance - command.extend(["-F"]) # option to avoid the daemonization of VPCS - if self.script_file: command.extend([os.path.basename(self.script_file)]) return command From 289b68e5c123248881b4f79754df180bd801c250 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 26 May 2015 09:48:01 +0200 Subject: [PATCH 028/336] Use setter for the qemu_path (allow to pass only the binary name) --- gns3server/modules/qemu/qemu_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index f8ae3b12..8c240b5a 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -70,7 +70,7 @@ class QemuVM(BaseVM): self._stdout_file = "" # QEMU VM settings - self._qemu_path = qemu_path + self.qemu_path = qemu_path self._hda_disk_image = "" self._hdb_disk_image = "" self._hdc_disk_image = "" From f1ab682be9f6b2f0aeee8b2c59416f8ae003504c Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 26 May 2015 11:18:56 +0200 Subject: [PATCH 029/336] Do not crash if module ioucon is loaded by tests on Windows --- gns3server/modules/iou/ioucon.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gns3server/modules/iou/ioucon.py b/gns3server/modules/iou/ioucon.py index 475109b6..47c542ed 100644 --- a/gns3server/modules/iou/ioucon.py +++ b/gns3server/modules/iou/ioucon.py @@ -21,10 +21,14 @@ import socket import sys import os import select -import fcntl +try: + import fcntl + import termios + import tty +except ImportError: + # On windows it's not available but this module can be included by the test suite + pass import struct -import termios -import tty import time import argparse import traceback From 117630c887c00e350a1515f1e0b2ceb4dd4bbda4 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 26 May 2015 11:51:24 +0200 Subject: [PATCH 030/336] Test ok on Windows --- tests/modules/vpcs/test_vpcs_vm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index 0d2b2266..416147ee 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -200,9 +200,9 @@ def test_update_startup_script_h(vm): def test_get_startup_script(vm): - content = os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) + content = "echo GNS3 VPCS\nip 192.168.1.2" vm.startup_script = content - assert vm.startup_script == "echo GNS3 VPCS\nip 192.168.1.2" + assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS","ip 192.168.1.2"]) def test_get_startup_script_using_default_script(vm): From 9ef4628cbc2312123c07d90904ca73dae1e327aa Mon Sep 17 00:00:00 2001 From: Vasil Rangelov Date: Sun, 10 May 2015 20:46:51 +0300 Subject: [PATCH 031/336] Added a method for getting qemu-img binaries, and moved the qemu folder into a separate method. --- gns3server/handlers/api/qemu_handler.py | 15 ++++++ gns3server/modules/qemu/__init__.py | 64 ++++++++++++++++++++++--- tests/modules/qemu/test_qemu_manager.py | 22 +++++++++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index 041525d1..426ca1a2 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -292,6 +292,21 @@ class QEMUHandler: binaries = yield from Qemu.binary_list() response.json(binaries) + @classmethod + @Route.get( + r"/qemu/img-binaries", + status_codes={ + 200: "Success", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Get a list of available Qemu-img binaries", + output=QEMU_BINARY_LIST_SCHEMA) + def list_img_binaries(request, response): + + binaries = yield from Qemu.img_binary_list() + response.json(binaries) + @Route.get( r"/qemu/vms", status_codes={ diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index 70c1bf55..6f569c1c 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -39,14 +39,13 @@ class Qemu(BaseManager): _VM_CLASS = QemuVM @staticmethod - def binary_list(): + def paths_list(): """ - Gets QEMU binaries list available on the host. + Gets a folder list of possibly available QEMU binaries on the host. - :returns: Array of dictionary {"path": Qemu binary path, "version": version of Qemu} + :returns: List of folders where Qemu binaries MAY reside. """ - qemus = [] paths = [] try: paths.append(os.getcwd()) @@ -72,14 +71,25 @@ class Qemu(BaseManager): paths.append(os.path.join(os.environ["PROGRAMFILES"], "qemu")) elif sys.platform.startswith("darwin"): # add specific locations on Mac OS X regardless of what's in $PATH - paths.extend(["/usr/local/bin", "/opt/local/bin"]) + paths.extend(["/usr/bin", "/usr/local/bin", "/opt/local/bin"]) if hasattr(sys, "frozen"): try: paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) # If the user run the server by hand from outside except FileNotFoundError: paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"]) - for path in paths: + return paths + + @staticmethod + def binary_list(): + """ + Gets QEMU binaries list available on the host. + + :returns: Array of dictionary {"path": Qemu binary path, "version": version of Qemu} + """ + + qemus = [] + for path in Qemu.paths_list(): try: for f in os.listdir(path): if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \ @@ -93,6 +103,28 @@ class Qemu(BaseManager): return qemus + @staticmethod + def img_binary_list(): + """ + Gets QEMU-img binaries list available on the host. + + :returns: Array of dictionary {"path": Qemu-img binary path, "version": version of Qemu-img} + """ + qemu_imgs = [] + for path in Qemu.paths_list(): + try: + for f in os.listdir(path): + if (f == "qemu-img" or f == "qemu-img.exe") and \ + os.access(os.path.join(path, f), os.X_OK) and \ + os.path.isfile(os.path.join(path, f)): + qemu_path = os.path.join(path, f) + version = yield from Qemu._get_qemu_img_version(qemu_path) + qemu_imgs.append({"path": qemu_path, "version": version}) + except OSError: + continue + + return qemu_imgs + @staticmethod @asyncio.coroutine def _get_qemu_version(qemu_path): @@ -115,6 +147,26 @@ class Qemu(BaseManager): except subprocess.SubprocessError as e: raise QemuError("Error while looking for the Qemu version: {}".format(e)) + @staticmethod + @asyncio.coroutine + def _get_qemu_img_version(qemu_img_path): + """ + Gets the Qemu-img version. + + :param qemu_img_path: path to Qemu-img executable. + """ + + try: + output = yield from subprocess_check_output(qemu_img_path, "--version") + match = re.search("version\s+([0-9a-z\-\.]+)", output) + if match: + version = match.group(1) + return version + else: + raise QemuError("Could not determine the Qemu-img version for {}".format(qemu_img_path)) + except subprocess.SubprocessError as e: + raise QemuError("Error while looking for the Qemu-img version: {}".format(e)) + @staticmethod def get_legacy_vm_workdir(legacy_vm_id, name): """ diff --git a/tests/modules/qemu/test_qemu_manager.py b/tests/modules/qemu/test_qemu_manager.py index 572fb347..8eb7a60b 100644 --- a/tests/modules/qemu/test_qemu_manager.py +++ b/tests/modules/qemu/test_qemu_manager.py @@ -57,6 +57,28 @@ def test_binary_list(loop): assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus +def test_img_binary_list(loop): + + files_to_create = ["qemu-img", "qemu-io", "qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello"] + + for file_to_create in files_to_create: + path = os.path.join(os.environ["PATH"], file_to_create) + with open(path, "w+") as f: + f.write("1") + os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + + with asyncio_patch("gns3server.modules.qemu.subprocess_check_output", return_value="qemu-img version 2.2.0, Copyright (c) 2004-2008 Fabrice Bellard") as mock: + qemus = loop.run_until_complete(asyncio.async(Qemu.img_binary_list())) + + version = "2.2.0" + + assert {"path": os.path.join(os.environ["PATH"], "qemu-img"), "version": version} in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-io"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus + def test_get_legacy_vm_workdir(): From 98e01ff21de8a745358f0095a9ec00bf0db931f3 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 27 May 2015 21:06:18 -0600 Subject: [PATCH 032/336] Serial console implementation for VMware VMs. --- .../modules/virtualbox/virtualbox_vm.py | 2 +- gns3server/modules/vmware/vmware_vm.py | 120 ++++++++++++++++-- .../virtualbox => utils}/telnet_server.py | 1 + 3 files changed, 109 insertions(+), 14 deletions(-) rename gns3server/{modules/virtualbox => utils}/telnet_server.py (99%) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 4ec0aa37..a306db85 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -29,11 +29,11 @@ import socket import asyncio from pkg_resources import parse_version +from gns3server.utils.telnet_server import TelnetServer from .virtualbox_error import VirtualBoxError from ..nios.nio_udp import NIOUDP from ..nios.nio_nat import NIONAT from ..adapters.ethernet_adapter import EthernetAdapter -from .telnet_server import TelnetServer # TODO: port TelnetServer to asyncio from ..base_vm import BaseVM if sys.platform.startswith('win'): diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 44694476..7dd82bd6 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -21,25 +21,30 @@ VMware VM instance. import sys import os +import socket import subprocess import configparser import shutil import asyncio +import tempfile from gns3server.utils.asyncio import wait_for_process_termination from gns3server.utils.asyncio import monitor_process +from gns3server.utils.telnet_server import TelnetServer from collections import OrderedDict from .vmware_error import VMwareError from ..nios.nio_udp import NIOUDP from ..adapters.ethernet_adapter import EthernetAdapter from ..base_vm import BaseVM +if sys.platform.startswith('win'): + import msvcrt + import win32file import logging log = logging.getLogger(__name__) - class VMwareVM(BaseVM): """ @@ -54,8 +59,11 @@ class VMwareVM(BaseVM): self._vmx_pairs = OrderedDict() self._ubridge_process = None self._ubridge_stdout_file = "" + self._telnet_server_thread = None + self._serial_pipe = None self._vmnets = [] self._maximum_adapters = 10 + self._started = False self._closed = False # VMware VM settings @@ -109,11 +117,6 @@ class VMwareVM(BaseVM): def _set_network_options(self): - try: - self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path) - except OSError as e: - raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) - # first do some sanity checks for adapter_number in range(0, self._adapters): connected = "ethernet{}.startConnected".format(adapter_number) @@ -179,7 +182,6 @@ class VMwareVM(BaseVM): log.debug("disabling remaining adapter {}".format(adapter_number)) self._vmx_pairs["ethernet{}.startConnected".format(adapter_number)] = "FALSE" - self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) self._update_ubridge_config() def _update_ubridge_config(self): @@ -242,7 +244,7 @@ class VMwareVM(BaseVM): :returns: path to uBridge """ - path = self._manager.config.get_section_config("VMware").get("ubridge_path", "ubridge") + path = self._manager.config.get_section_config("Server").get("ubridge_path", "ubridge") if path == "ubridge": path = shutil.which("ubridge") return path @@ -333,13 +335,26 @@ class VMwareVM(BaseVM): if not ubridge_path or not os.path.isfile(ubridge_path): raise VMwareError("ubridge is necessary to start a VMware VM") + try: + self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path) + except OSError as e: + raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) + self._set_network_options() - yield from self._start_ubridge() + self._set_serial_console() + self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) + yield from self._start_ubridge() if self._headless: yield from self._control_vm("start", "nogui") else: yield from self._control_vm("start") + + if self._enable_remote_console and self._console is not None: + yield from asyncio.sleep(1) # give some time to VMware to create the pipe file. + self._start_remote_console() + + self._started = True log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id)) @asyncio.coroutine @@ -348,6 +363,7 @@ class VMwareVM(BaseVM): Stops this VMware VM. """ + self._stop_remote_console() if self.is_ubridge_running(): self._terminate_process_ubridge() try: @@ -361,9 +377,8 @@ class VMwareVM(BaseVM): try: yield from self._control_vm("stop") finally: - + self._started = False self._vmnets.clear() - try: self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path) except OSError as e: @@ -518,10 +533,11 @@ class VMwareVM(BaseVM): if enable_remote_console: log.info("VMware VM '{name}' [{id}] has enabled the console".format(name=self.name, id=self.id)) - #self._start_remote_console() + if self._started: + self._start_remote_console() else: log.info("VMware VM '{name}' [{id}] has disabled the console".format(name=self.name, id=self.id)) - #self._stop_remote_console() + self._stop_remote_console() self._enable_remote_console = enable_remote_console @property @@ -647,3 +663,81 @@ class VMwareVM(BaseVM): nio=nio, adapter_number=adapter_number)) return nio + + def _get_pipe_name(self): + """ + Returns the pipe name to create a serial connection. + + :returns: pipe path (string) + """ + + if sys.platform.startswith("win"): + pipe_name = r"\\.\pipe\gns3_vmware\{}".format(self.id) + else: + pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vmware", "{}".format(self.id)) + try: + os.makedirs(os.path.dirname(pipe_name), exist_ok=True) + except OSError as e: + raise VMwareError("Could not create the VMware pipe directory: {}".format(e)) + return pipe_name + + def _set_serial_console(self): + """ + Configures the first serial port to allow a serial console connection. + """ + + pipe_name = self._get_pipe_name() + serial_port = {"serial0.present": "TRUE", + "serial0.fileType": "pipe", + "serial0.fileName": pipe_name, + "serial0.pipe.endPoint": "server"} + self._vmx_pairs.update(serial_port) + + def _start_remote_console(self): + """ + Starts remote console support for this VM. + """ + + # starts the Telnet to pipe thread + pipe_name = self._get_pipe_name() + if sys.platform.startswith("win"): + try: + self._serial_pipe = open(pipe_name, "a+b") + except OSError as e: + raise VMwareError("Could not open the pipe {}: {}".format(pipe_name, e)) + try: + self._telnet_server_thread = TelnetServer(self.name, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._manager.port_manager.console_host, self._console) + except OSError as e: + raise VMwareError("Unable to create Telnet server: {}".format(e)) + self._telnet_server_thread.start() + else: + try: + self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._serial_pipe.connect(pipe_name) + except OSError as e: + raise VMwareError("Could not connect to the pipe {}: {}".format(pipe_name, e)) + try: + self._telnet_server_thread = TelnetServer(self.name, self._serial_pipe, self._manager.port_manager.console_host, self._console) + except OSError as e: + raise VMwareError("Unable to create Telnet server: {}".format(e)) + self._telnet_server_thread.start() + + def _stop_remote_console(self): + """ + Stops remote console support for this VM. + """ + + if self._telnet_server_thread: + if self._telnet_server_thread.is_alive(): + self._telnet_server_thread.stop() + self._telnet_server_thread.join(timeout=3) + if self._telnet_server_thread.is_alive(): + log.warn("Serial pipe thread is still alive!") + self._telnet_server_thread = None + + if self._serial_pipe: + if sys.platform.startswith("win"): + win32file.CloseHandle(msvcrt.get_osfhandle(self._serial_pipe.fileno())) + else: + self._serial_pipe.close() + self._serial_pipe = None diff --git a/gns3server/modules/virtualbox/telnet_server.py b/gns3server/utils/telnet_server.py similarity index 99% rename from gns3server/modules/virtualbox/telnet_server.py rename to gns3server/utils/telnet_server.py index bfbee71b..a5d3ef4d 100644 --- a/gns3server/modules/virtualbox/telnet_server.py +++ b/gns3server/utils/telnet_server.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# TODO: port TelnetServer to asyncio import sys import time From 57a069b0278ee2e6c16f6b5f2ca8ce5c8888846d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 28 May 2015 11:29:30 +0200 Subject: [PATCH 033/336] Support cacert.pem in the new frozen package --- gns3server/crash_report.py | 3 ++- gns3server/utils/get_resource.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 2cefe0e4..a18d4335 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -33,6 +33,7 @@ except ImportError: from .version import __version__ from .config import Config +from .utils.get_resource import get_resource import logging log = logging.getLogger(__name__) @@ -46,7 +47,7 @@ class CrashReport: DSN = "sync+https://9e6f04df72c74b6894a6dcd2928d069e:2035d1beb1654136b170f1e91f05ee51@app.getsentry.com/38482" if hasattr(sys, "frozen"): - cacert = os.path.join(os.getcwd(), "cacert.pem") + cacert = get_resource("cacert.pem") if os.path.isfile(cacert): DSN += "?ca_certs={}".format(cacert) else: diff --git a/gns3server/utils/get_resource.py b/gns3server/utils/get_resource.py index dae285fc..04d44fac 100644 --- a/gns3server/utils/get_resource.py +++ b/gns3server/utils/get_resource.py @@ -19,6 +19,8 @@ import tempfile import pkg_resources import atexit import logging +import os +import sys log = logging.getLogger(__name__) @@ -39,3 +41,19 @@ def clean_egg_cache(): except Exception: # We don't care if we can not cleanup pass + + +def get_resource(resource_name): + """ + Return a resource in current directory or in frozen package + """ + + resource_path = None + if hasattr(sys, "frozen") and sys.platform.startswith("darwin"): + resource_name = os.path.join("../Resources", resource_name) + if hasattr(sys, "frozen") and os.path.exists(resource_name): + resource_path = os.path.normpath(os.path.join(os.getcwd(), resource_name)) + elif not hasattr(sys, "frozen") and pkg_resources.resource_exists("gns3", resource_name): + resource_path = pkg_resources.resource_filename("gns3", resource_name) + resource_path = os.path.normpath(resource_path) + return resource_path From a79249aa9e4cca3201e81e80ae9c117f89dda7c7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 28 May 2015 12:05:19 +0200 Subject: [PATCH 034/336] Fix tests after merge --- tests/modules/virtualbox/test_virtualbox_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/modules/virtualbox/test_virtualbox_manager.py b/tests/modules/virtualbox/test_virtualbox_manager.py index 84e06870..8d453137 100644 --- a/tests/modules/virtualbox/test_virtualbox_manager.py +++ b/tests/modules/virtualbox/test_virtualbox_manager.py @@ -71,7 +71,7 @@ def test_vboxmanage_path(manager, tmpdir): assert manager.find_vboxmanage() == path -def test_get_list(manager, loop): +def test_list_images(manager, loop): vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', '"Carriage', 'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', @@ -91,7 +91,7 @@ def test_get_list(manager, loop): with asyncio_patch("gns3server.modules.virtualbox.VirtualBox.execute") as mock: mock.side_effect = execute_mock - vms = loop.run_until_complete(asyncio.async(manager.get_list())) + vms = loop.run_until_complete(asyncio.async(manager.list_images())) assert vms == [ {"vmname": "Windows 8.1", "ram": 512}, {"vmname": "Linux Microcore 4.7.1", "ram": 256} From ada94d486ab637bd4714c9bf9230554b1456fe4d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 28 May 2015 13:24:45 +0200 Subject: [PATCH 035/336] Get server resource independant of working directory --- gns3server/crash_report.py | 2 +- gns3server/utils/get_resource.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index fbbcfe5f..fd6621ef 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -55,7 +55,7 @@ class CrashReport: DSN = "sync+https://9e6f04df72c74b6894a6dcd2928d069e:2035d1beb1654136b170f1e91f05ee51@app.getsentry.com/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") - if os.path.isfile(cacert): + if cacert is not None and os.path.isfile(cacert): DSN += "?ca_certs={}".format(cacert) else: log.warning("The SSL certificate bundle file '{}' could not be found".format(cacert)) diff --git a/gns3server/utils/get_resource.py b/gns3server/utils/get_resource.py index 04d44fac..372f4ce4 100644 --- a/gns3server/utils/get_resource.py +++ b/gns3server/utils/get_resource.py @@ -50,10 +50,10 @@ def get_resource(resource_name): resource_path = None if hasattr(sys, "frozen") and sys.platform.startswith("darwin"): - resource_name = os.path.join("../Resources", resource_name) + resource_name = os.path.join(os.path.dirname(sys.executable), "../Resources", resource_name) if hasattr(sys, "frozen") and os.path.exists(resource_name): - resource_path = os.path.normpath(os.path.join(os.getcwd(), resource_name)) - elif not hasattr(sys, "frozen") and pkg_resources.resource_exists("gns3", resource_name): - resource_path = pkg_resources.resource_filename("gns3", resource_name) + resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name)) + elif not hasattr(sys, "frozen") and pkg_resources.resource_exists("gns3server", resource_name): + resource_path = pkg_resources.resource_filename("gns3server", resource_name) resource_path = os.path.normpath(resource_path) return resource_path From a60389427be696b2fbd9a615b6c6584bdfca0d4c Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 30 May 2015 20:26:38 -0600 Subject: [PATCH 036/336] Support for VMware linked clones. --- gns3server/modules/vmware/__init__.py | 126 +++++++++++++++++-------- gns3server/modules/vmware/vmware_vm.py | 96 ++++++++++++++++++- 2 files changed, 182 insertions(+), 40 deletions(-) diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 780b7597..f808561e 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -44,6 +44,7 @@ class VMware(BaseManager): def __init__(self): super().__init__() + self._execute_lock = asyncio.Lock() self._vmrun_path = None self._vmnets = [] self._vmnet_start_range = 2 @@ -167,31 +168,32 @@ class VMware(BaseManager): @asyncio.coroutine def execute(self, subcommand, args, timeout=60, host_type=None): - vmrun_path = self.vmrun_path - if not vmrun_path: - vmrun_path = self.find_vmrun() - if host_type is None: - host_type = self.host_type - - command = [vmrun_path, "-T", host_type, subcommand] - command.extend(args) - log.debug("Executing vmrun with command: {}".format(command)) - try: - process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) - except (OSError, subprocess.SubprocessError) as e: - raise VMwareError("Could not execute vmrun: {}".format(e)) - - try: - stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout) - except asyncio.TimeoutError: - raise VMwareError("vmrun has timed out after {} seconds!".format(timeout)) - - if process.returncode: - # vmrun print errors on stdout - vmrun_error = stdout_data.decode("utf-8", errors="ignore") - raise VMwareError("vmrun has returned an error: {}".format(vmrun_error)) - - return stdout_data.decode("utf-8", errors="ignore").splitlines() + with (yield from self._execute_lock): + vmrun_path = self.vmrun_path + if not vmrun_path: + vmrun_path = self.find_vmrun() + if host_type is None: + host_type = self.host_type + + command = [vmrun_path, "-T", host_type, subcommand] + command.extend(args) + log.debug("Executing vmrun with command: {}".format(command)) + try: + process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + except (OSError, subprocess.SubprocessError) as e: + raise VMwareError("Could not execute vmrun: {}".format(e)) + + try: + stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout) + except asyncio.TimeoutError: + raise VMwareError("vmrun has timed out after {} seconds!".format(timeout)) + + if process.returncode: + # vmrun print errors on stdout + vmrun_error = stdout_data.decode("utf-8", errors="ignore") + raise VMwareError("vmrun has returned an error: {}".format(vmrun_error)) + + return stdout_data.decode("utf-8", errors="ignore").splitlines() @staticmethod def parse_vmware_file(path): @@ -213,6 +215,20 @@ class VMware(BaseManager): continue return pairs + @staticmethod + def write_vmware_file(path, pairs): + """ + Write a VMware file (excepting VMX file). + + :param path: path to the VMware file + :param pairs: settings to write + """ + + with open(path, "w", encoding="utf-8") as f: + for key, value in pairs.items(): + entry = '{} = "{}"\n'.format(key, value) + f.write(entry) + @staticmethod def write_vmx_file(path, pairs): """ @@ -289,31 +305,63 @@ class VMware(BaseManager): continue return vms - def list_vms(self): + @staticmethod + def get_vmware_inventory_path(): """ - Gets VMware VM list. + Returns VMware inventory file path. + + :returns: path to the inventory file """ if sys.platform.startswith("win"): - inventory_path = os.path.expandvars(r"%APPDATA%\Vmware\Inventory.vmls") + return os.path.expandvars(r"%APPDATA%\Vmware\Inventory.vmls") elif sys.platform.startswith("darwin"): - inventory_path = os.path.expanduser("~/Library/Application\ Support/VMware Fusion/vmInventory") + return os.path.expanduser("~/Library/Application\ Support/VMware Fusion/vmInventory") else: - inventory_path = os.path.expanduser("~/.vmware/inventory.vmls") + return os.path.expanduser("~/.vmware/inventory.vmls") + @staticmethod + def get_vmware_preferences_path(): + """ + Returns VMware preferences file path. + + :returns: path to the preferences file + """ + + if sys.platform.startswith("win"): + return os.path.expandvars(r"%APPDATA%\VMware\preferences.ini") + elif sys.platform.startswith("darwin"): + return os.path.expanduser("~/Library/Preferences/VMware Fusion/preferences") + else: + return os.path.expanduser("~/.vmware/preferences") + + @staticmethod + def get_vmware_default_vm_path(): + """ + Returns VMware default VM directory path. + + :returns: path to the default VM directory + """ + + if sys.platform.startswith("win"): + return os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") + elif sys.platform.startswith("darwin"): + return os.path.expanduser("~/Documents/Virtual Machines.localized") + else: + return os.path.expanduser("~/vmware") + + def list_vms(self): + """ + Gets VMware VM list. + """ + + inventory_path = self.get_vmware_inventory_path() if os.path.exists(inventory_path): return self._get_vms_from_inventory(inventory_path) else: # VMware player has no inventory file, let's search the default location for VMs. - if sys.platform.startswith("win"): - vmware_preferences_path = os.path.expandvars(r"%APPDATA%\VMware\preferences.ini") - default_vm_path = os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") - elif sys.platform.startswith("darwin"): - vmware_preferences_path = os.path.expanduser("~/Library/Preferences/VMware Fusion/preferences") - default_vm_path = os.path.expanduser("~/Documents/Virtual Machines.localized") - else: - vmware_preferences_path = os.path.expanduser("~/.vmware/preferences") - default_vm_path = os.path.expanduser("~/vmware") + vmware_preferences_path = self.get_vmware_preferences_path() + default_vm_path = self.get_vmware_default_vm_path() if os.path.exists(vmware_preferences_path): # the default vm path may be present in VMware preferences file. diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index 7dd82bd6..fe2bef48 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -105,6 +105,71 @@ class VMwareVM(BaseVM): log.debug("Control VM '{}' result: {}".format(subcommand, result)) return result + @asyncio.coroutine + def create(self): + + if self._linked_clone and not os.path.exists(os.path.join(self.working_dir, os.path.basename(self._vmx_path))): + # create the base snapshot for linked clones + base_snapshot_name = "GNS3 Linked Base for clones" + vmsd_path = os.path.splitext(self._vmx_path)[0] + ".vmsd" + if not os.path.exists(vmsd_path): + raise VMwareError("{} doesn't not exist".format(vmsd_path)) + try: + vmsd_pairs = self.manager.parse_vmware_file(vmsd_path) + except OSError as e: + raise VMwareError('Could not read VMware VMSD file "{}": {}'.format(vmsd_path, e)) + gns3_snapshot_exists = False + for value in vmsd_pairs.values(): + if value == base_snapshot_name: + gns3_snapshot_exists = True + break + if not gns3_snapshot_exists: + log.info("Creating snapshot '{}'".format(base_snapshot_name)) + yield from self._control_vm("snapshot", base_snapshot_name) + + # create the linked clone based on the base snapshot + new_vmx_path = os.path.join(self.working_dir, self.name + ".vmx") + yield from self._control_vm("clone", + new_vmx_path, + "linked", + "-snapshot={}".format(base_snapshot_name), + "-cloneName={}".format(self.name)) + + try: + vmsd_pairs = self.manager.parse_vmware_file(vmsd_path) + except OSError as e: + raise VMwareError('Could not read VMware VMSD file "{}": {}'.format(vmsd_path, e)) + + snapshot_name = None + for name, value in vmsd_pairs.items(): + if value == base_snapshot_name: + snapshot_name = name.split(".", 1)[0] + break + + if snapshot_name is None: + raise VMwareError("Could not find the linked base snapshot in {}".format(vmsd_path)) + + num_clones_entry = "{}.numClones".format(snapshot_name) + if num_clones_entry in vmsd_pairs: + try: + nb_of_clones = int(vmsd_pairs[num_clones_entry]) + except ValueError: + raise VMwareError("Value of {} in {} is not a number".format(num_clones_entry, vmsd_path)) + vmsd_pairs[num_clones_entry] = str(nb_of_clones - 1) + + for clone_nb in range(0, nb_of_clones): + clone_entry = "{}.clone{}".format(snapshot_name, clone_nb) + if clone_entry in vmsd_pairs: + del vmsd_pairs[clone_entry] + + try: + self.manager.write_vmware_file(vmsd_path, vmsd_pairs) + except OSError as e: + raise VMwareError('Could not write VMware VMSD file "{}": {}'.format(vmsd_path, e)) + + # update the VMX file path + self._vmx_path = new_vmx_path + def _get_vmx_setting(self, name, value=None): if name in self._vmx_pairs: @@ -342,7 +407,11 @@ class VMwareVM(BaseVM): self._set_network_options() self._set_serial_console() - self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) + + try: + self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) + except OSError as e: + raise VMwareError('Could not write VMware VMX file "{}": {}'.format(self._vmx_path, e)) yield from self._start_ubridge() if self._headless: @@ -465,6 +534,31 @@ class VMwareVM(BaseVM): except VMwareError: pass + if self._linked_clone: + # clean the VMware inventory path from this linked clone + inventory_path = self.manager.get_vmware_inventory_path() + if os.path.exists(inventory_path): + try: + inventory_pairs = self.manager.parse_vmware_file(inventory_path) + except OSError as e: + log.warning('Could not read VMware inventory file "{}": {}'.format(inventory_path, e)) + + vmlist_entry = None + for name, value in inventory_pairs.items(): + if value == self._vmx_path: + vmlist_entry = name.split(".", 1)[0] + break + + if vmlist_entry is not None: + for name in inventory_pairs.keys(): + if name.startswith(vmlist_entry): + del inventory_pairs[name] + + try: + self.manager.write_vmware_file(inventory_path, inventory_pairs) + except OSError as e: + raise VMwareError('Could not write VMware inventory file "{}": {}'.format(inventory_path, e)) + log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id)) self._closed = True From edff4474838ebca9faa2897a33a40c25463bfe7d Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Jun 2015 21:54:08 -0600 Subject: [PATCH 037/336] Catch exception in snapshot dialog. --- gns3server/modules/port_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gns3server/modules/port_manager.py b/gns3server/modules/port_manager.py index 76248cd7..0422e4ec 100644 --- a/gns3server/modules/port_manager.py +++ b/gns3server/modules/port_manager.py @@ -16,7 +16,6 @@ # along with this program. If not, see . import socket -import sys import ipaddress from aiohttp.web import HTTPConflict from gns3server.config import Config From 933bc5a7b01f0291642993f870fb592bfb9b7cf5 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 2 Jun 2015 16:30:35 -0600 Subject: [PATCH 038/336] ACPI shutdown support for VirtualBox VMs. --- .../modules/virtualbox/virtualbox_vm.py | 39 +++++++++++++++++-- gns3server/schemas/virtualbox.py | 12 ++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index ac1bb2dd..33452363 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -65,6 +65,7 @@ class VirtualBoxVM(BaseVM): self._adapters = adapters self._ethernet_adapters = {} self._headless = False + self._acpi_shutdown = False self._enable_remote_console = False self._vmname = vmname self._use_any_adapter = False @@ -79,6 +80,7 @@ class VirtualBoxVM(BaseVM): "project_id": self.project.id, "vmname": self.vmname, "headless": self.headless, + "acpi_shutdown": self.acpi_shutdown, "enable_remote_console": self.enable_remote_console, "adapters": self._adapters, "adapter_type": self.adapter_type, @@ -205,11 +207,16 @@ class VirtualBoxVM(BaseVM): self._stop_remote_console() vm_state = yield from self._get_vm_state() if vm_state == "running" or vm_state == "paused" or vm_state == "stuck": - # power off the VM - result = yield from self._control_vm("poweroff") - log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) - log.debug("Stop result: {}".format(result)) + if self.acpi_shutdown: + # use ACPI to shutdown the VM + result = yield from self._control_vm("acpipowerbutton") + log.debug("ACPI shutdown result: {}".format(result)) + else: + # power off the VM + result = yield from self._control_vm("poweroff") + log.debug("Stop result: {}".format(result)) + log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) # yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM try: # deactivate the first serial port @@ -389,6 +396,30 @@ class VirtualBoxVM(BaseVM): log.info("VirtualBox VM '{name}' [{id}] has disabled the headless mode".format(name=self.name, id=self.id)) self._headless = headless + @property + def acpi_shutdown(self): + """ + Returns either the VM will use ACPI shutdown + + :returns: boolean + """ + + return self._acpi_shutdown + + @acpi_shutdown.setter + def acpi_shutdown(self, acpi_shutdown): + """ + Sets either the VM will use ACPI shutdown + + :param acpi_shutdown: boolean + """ + + if acpi_shutdown: + log.info("VirtualBox VM '{name}' [{id}] has enabled the ACPI shutdown mode".format(name=self.name, id=self.id)) + else: + log.info("VirtualBox VM '{name}' [{id}] has disabled the ACPI shutdown mode".format(name=self.name, id=self.id)) + self._acpi_shutdown = acpi_shutdown + @property def enable_remote_console(self): """ diff --git a/gns3server/schemas/virtualbox.py b/gns3server/schemas/virtualbox.py index 8e57c35c..b91e3736 100644 --- a/gns3server/schemas/virtualbox.py +++ b/gns3server/schemas/virtualbox.py @@ -80,6 +80,10 @@ VBOX_CREATE_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "acpi_shutdown": { + "description": "ACPI shutdown", + "type": "boolean" + }, }, "additionalProperties": False, "required": ["name", "vmname", "linked_clone"], @@ -135,6 +139,10 @@ VBOX_UPDATE_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "acpi_shutdown": { + "description": "ACPI shutdown", + "type": "boolean" + }, }, "additionalProperties": False, } @@ -191,6 +199,10 @@ VBOX_OBJECT_SCHEMA = { "description": "headless mode", "type": "boolean" }, + "acpi_shutdown": { + "description": "ACPI shutdown", + "type": "boolean" + }, "adapters": { "description": "number of adapters", "type": "integer", From 743f1392b56936fb72c7e0991cbd65df82da4563 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 2 Jun 2015 22:33:38 -0600 Subject: [PATCH 039/336] ACPI shutdown support for Qemu VMs. --- gns3server/modules/qemu/qemu_vm.py | 40 ++++++++++++++++--- .../modules/virtualbox/virtualbox_vm.py | 1 + gns3server/schemas/qemu.py | 17 ++++++-- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index f27c2518..d7d2bf12 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -27,6 +27,7 @@ import subprocess import shlex import asyncio import socket +import gns3server from .qemu_error import QemuError from ..adapters.ethernet_adapter import EthernetAdapter @@ -36,7 +37,6 @@ from ..nios.nio_nat import NIONAT from ..base_vm import BaseVM from ...schemas.qemu import QEMU_OBJECT_SCHEMA from ...utils.asyncio import monitor_process -from ...config import Config import logging log = logging.getLogger(__name__) @@ -83,6 +83,7 @@ class QemuVM(BaseVM): self._kernel_image = "" self._kernel_command_line = "" self._legacy_networking = False + self._acpi_shutdown = False self._cpu_throttling = 0 # means no CPU throttling self._process_priority = "low" @@ -299,6 +300,30 @@ class QemuVM(BaseVM): log.info('QEMU VM "{name}" [{id}] has disabled legacy networking'.format(name=self._name, id=self._id)) self._legacy_networking = legacy_networking + @property + def acpi_shutdown(self): + """ + Returns either this QEMU VM can be ACPI shutdown. + + :returns: boolean + """ + + return self._acpi_shutdown + + @acpi_shutdown.setter + def acpi_shutdown(self, acpi_shutdown): + """ + Sets either this QEMU VM can be ACPI shutdown. + + :param acpi_shutdown: boolean + """ + + if acpi_shutdown: + log.info('QEMU VM "{name}" [{id}] has enabled ACPI shutdown'.format(name=self._name, id=self._id)) + else: + log.info('QEMU VM "{name}" [{id}] has disabled ACPI shutdown'.format(name=self._name, id=self._id)) + self._acpi_shutdown = acpi_shutdown + @property def cpu_throttling(self): """ @@ -616,14 +641,18 @@ class QemuVM(BaseVM): if self.is_running(): log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid)) try: - self._process.terminate() - self._process.wait() - except subprocess.TimeoutExpired: + if self.acpi_shutdown: + yield from self._control_vm("system_powerdown") + yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30) + else: + self._process.terminate() + yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3) + except asyncio.TimeoutError: self._process.kill() if self._process.returncode is None: log.warn('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid)) - self._process = None self.status = "stopped" + self._process = None self._stop_cpulimit() @asyncio.coroutine @@ -674,6 +703,7 @@ class QemuVM(BaseVM): """ log.debug('QEMU VM "{name}" [{id}] is closing'.format(name=self._name, id=self._id)) + self.acpi_shutdown = False yield from self.stop() if self._console: self._manager.port_manager.release_tcp_port(self._console, self._project) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 33452363..ab0911f7 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -325,6 +325,7 @@ class VirtualBoxVM(BaseVM): if nio and isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) + self.acpi_shutdown = False yield from self.stop() if self._linked_clone: diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 3385c3a9..7f597ca6 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -94,6 +94,10 @@ QEMU_CREATE_SCHEMA = { "description": "Use QEMU legagy networking commands (-net syntax)", "type": ["boolean", "null"], }, + "acpi_shutdown": { + "description": "ACPI shutdown support", + "type": ["boolean", "null"], + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -187,6 +191,10 @@ QEMU_UPDATE_SCHEMA = { "description": "Use QEMU legagy networking commands (-net syntax)", "type": ["boolean", "null"], }, + "acpi_shutdown": { + "description": "ACPI shutdown support", + "type": ["boolean", "null"], + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -289,6 +297,10 @@ QEMU_OBJECT_SCHEMA = { "description": "Use QEMU legagy networking commands (-net syntax)", "type": "boolean", }, + "acpi_shutdown": { + "description": "ACPI shutdown support", + "type": "boolean", + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -312,9 +324,8 @@ QEMU_OBJECT_SCHEMA = { "additionalProperties": False, "required": ["vm_id", "project_id", "name", "qemu_path", "hda_disk_image", "hdb_disk_image", "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "console", - "initrd", "kernel_image", "kernel_command_line", - "legacy_networking", "cpu_throttling", "process_priority", "options" - ] + "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "acpi_shutdown", + "cpu_throttling", "process_priority", "options"] } QEMU_BINARY_LIST_SCHEMA = { From d8fb33dba22cc1894948984c4a85b824e52b6538 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 3 Jun 2015 18:58:17 +0200 Subject: [PATCH 040/336] Fix merge conflict --- gns3server/modules/qemu/__init__.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index af1b9c95..d8e2b149 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -46,12 +46,8 @@ class Qemu(BaseManager): :returns: List of folders where Qemu binaries MAY reside. """ -<<<<<<< HEAD - paths = [] -======= qemus = [] paths = set() ->>>>>>> master try: paths.add(os.getcwd()) except FileNotFoundError: @@ -76,18 +72,13 @@ class Qemu(BaseManager): paths.add(os.path.join(os.environ["PROGRAMFILES"], "qemu")) elif sys.platform.startswith("darwin"): # add specific locations on Mac OS X regardless of what's in $PATH -<<<<<<< HEAD - paths.extend(["/usr/bin", "/usr/local/bin", "/opt/local/bin"]) -======= - paths.update(["/usr/local/bin", "/opt/local/bin"]) ->>>>>>> master + paths.update(["/usr/bin", "/usr/local/bin", "/opt/local/bin"]) if hasattr(sys, "frozen"): try: paths.add(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) # If the user run the server by hand from outside except FileNotFoundError: -<<<<<<< HEAD - paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"]) + paths.add("/Applications/GNS3.app/Contents/Resources/qemu/bin") return paths @staticmethod @@ -100,10 +91,6 @@ class Qemu(BaseManager): qemus = [] for path in Qemu.paths_list(): -======= - paths.add("/Applications/GNS3.app/Contents/Resources/qemu/bin") - for path in paths: ->>>>>>> master try: for f in os.listdir(path): if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \ From 9f15fdbc2b6935c1db3c3979ca0c932035ffce11 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 3 Jun 2015 12:08:11 -0600 Subject: [PATCH 041/336] Fixes #181 (drop Python 3.3). --- gns3server/run.py | 6 +++--- setup.py | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/gns3server/run.py b/gns3server/run.py index 52afb695..7a76fd9f 100644 --- a/gns3server/run.py +++ b/gns3server/run.py @@ -199,9 +199,9 @@ def run(): if server_config.getboolean("local"): log.warning("Local mode is enabled. Beware, clients will have full control on your filesystem") - # we only support Python 3 version >= 3.3 - if sys.version_info < (3, 3): - raise RuntimeError("Python 3.3 or higher is required") + # we only support Python 3 version >= 3.4 + if sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or higher is required") user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format( major=sys.version_info[0], minor=sys.version_info[1], diff --git a/setup.py b/setup.py index 5f4b54cd..3e0d9fe3 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,9 @@ import sys from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand +# we only support Python 3 version >= 3.4 +if sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or higher is required") class PyTest(TestCommand): def finalize_options(self): @@ -42,7 +45,7 @@ dependencies = [ ] -if sys.version_info == (3, 3): +if sys.version_info == (3, 4): dependencies.append("asyncio>=3.4.2") setup( @@ -75,8 +78,8 @@ setup( "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: CPython", ], ) From 1c803f7afb3bba435fb56ceb6cc2b555e5ad915e Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 3 Jun 2015 12:10:29 -0600 Subject: [PATCH 042/336] Remove unneeded dependency line in setup.py --- setup.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setup.py b/setup.py index 3e0d9fe3..37a3b915 100644 --- a/setup.py +++ b/setup.py @@ -44,10 +44,6 @@ dependencies = [ "raven>=5.2.0" ] - -if sys.version_info == (3, 4): - dependencies.append("asyncio>=3.4.2") - setup( name="gns3-server", version=__import__("gns3server").__version__, From 37ddff9f07ad999fa3b756ea3787f25258917947 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 3 Jun 2015 14:52:49 -0600 Subject: [PATCH 043/336] Support for base MAC address for Qemu VMs. --- gns3server/modules/qemu/qemu_vm.py | 29 +++++++++++++++++++++++++++-- gns3server/schemas/qemu.py | 20 +++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index d7d2bf12..7d0a164d 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -75,6 +75,7 @@ class QemuVM(BaseVM): self._hdb_disk_image = "" self._hdc_disk_image = "" self._hdd_disk_image = "" + self._mac_address = "00:00:ab:%s:%s:00" % (self.id[-4:-2], self.id[-2:]) self._options = "" self._ram = 256 self._ethernet_adapters = [] @@ -276,6 +277,31 @@ class QemuVM(BaseVM): id=self._id, adapter_type=adapter_type)) + @property + def mac_address(self): + """ + Returns the MAC address for this QEMU VM. + + :returns: adapter type (string) + """ + + return self._mac_address + + @mac_address.setter + def mac_address(self, mac_address): + """ + Sets the MAC address for this QEMU VM. + + :param mac_address: MAC address + """ + + self._mac_address = mac_address + + log.info('QEMU VM "{name}" [{id}]: MAC address changed to {mac_addr}'.format(name=self._name, + id=self._id, + mac_addr=mac_address)) + + @property def legacy_networking(self): """ @@ -1033,8 +1059,7 @@ class QemuVM(BaseVM): network_options = [] network_options.extend(["-net", "none"]) # we do not want any user networking back-end if no adapter is connected. for adapter_number, adapter in enumerate(self._ethernet_adapters): - # TODO: let users specify a base mac address - mac = "00:00:ab:%s:%s:%02x" % (self.id[-4:-2], self.id[-2:], adapter_number) + mac = "%s%02x" % (self._mac_address[:-2], (int(self._mac_address[-2:]) + adapter_number) % 255) nio = adapter.get_nio(0) if self._legacy_networking: # legacy QEMU networking syntax (-net) diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 7f597ca6..f4f97685 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -78,6 +78,12 @@ QEMU_CREATE_SCHEMA = { "type": ["string", "null"], "minLength": 1, }, + "mac_address": { + "description": "QEMU MAC address", + "type": ["string", "null"], + "minLength": 1, + "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$" + }, "initrd": { "description": "QEMU initrd path", "type": ["string", "null"], @@ -175,6 +181,12 @@ QEMU_UPDATE_SCHEMA = { "type": ["string", "null"], "minLength": 1, }, + "mac_address": { + "description": "QEMU MAC address", + "type": ["string", "null"], + "minLength": 1, + "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$" + }, "initrd": { "description": "QEMU initrd path", "type": ["string", "null"], @@ -275,6 +287,12 @@ QEMU_OBJECT_SCHEMA = { "type": "string", "minLength": 1, }, + "mac_address": { + "description": "QEMU MAC address", + "type": "string", + "minLength": 1, + "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$" + }, "console": { "description": "console TCP port", "minimum": 1, @@ -323,7 +341,7 @@ QEMU_OBJECT_SCHEMA = { }, "additionalProperties": False, "required": ["vm_id", "project_id", "name", "qemu_path", "hda_disk_image", "hdb_disk_image", - "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "console", + "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "mac_address", "console", "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "acpi_shutdown", "cpu_throttling", "process_priority", "options"] } From b118f9a0782c111a82ea7e425fbc931c1bd0876e Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 3 Jun 2015 19:58:58 -0600 Subject: [PATCH 044/336] Replace RuntimeError by SystemExit. --- gns3server/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/run.py b/gns3server/run.py index 7a76fd9f..6048322e 100644 --- a/gns3server/run.py +++ b/gns3server/run.py @@ -201,7 +201,7 @@ def run(): # we only support Python 3 version >= 3.4 if sys.version_info < (3, 4): - raise RuntimeError("Python 3.4 or higher is required") + raise SystemExit("Python 3.4 or higher is required") user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format( major=sys.version_info[0], minor=sys.version_info[1], From ebfe00388569f8dabbbb758df7c86d259b6fa376 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 3 Jun 2015 20:00:08 -0600 Subject: [PATCH 045/336] Tool to quickly add and remove VMware vmnet interfaces on Linux. --- setup.py | 4 +- utils/__init__.py | 1 + utils/vmnet.py | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 utils/__init__.py create mode 100644 utils/vmnet.py diff --git a/setup.py b/setup.py index 37a3b915..59a3d669 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ from setuptools.command.test import test as TestCommand # we only support Python 3 version >= 3.4 if sys.version_info < (3, 4): - raise RuntimeError("Python 3.4 or higher is required") + raise SystemExit("Python 3.4 or higher is required") class PyTest(TestCommand): def finalize_options(self): @@ -57,7 +57,7 @@ setup( entry_points={ "console_scripts": [ "gns3server = gns3server.main:main", - "gns3dms = gns3dms.main:main", + "gns3vmnet = utils.vmnet:main", ] }, packages=find_packages(".", exclude=["docs", "tests"]), diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 00000000..0817d8f2 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +__author__ = 'grossmj' diff --git a/utils/vmnet.py b/utils/vmnet.py new file mode 100644 index 00000000..3fbc8357 --- /dev/null +++ b/utils/vmnet.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import os +import ipaddress +import argparse + +from collections import OrderedDict + +VMWARE_NETWORKING_FILE = "/etc/vmware/networking" +DEFAULT_RANGE = [10, 100] + + +def parse_networking_file(): + """ + Parse the VMware networking file. + """ + + pairs = dict() + allocated_subnets = [] + try: + with open(VMWARE_NETWORKING_FILE, "r", encoding="utf-8") as f: + version = f.readline() + for line in f.read().splitlines(): + try: + _, key, value = line.split(' ', 3) + key = key.strip() + value = value.strip() + pairs[key] = value + if key.endswith("HOSTONLY_SUBNET"): + allocated_subnets.append(value) + except ValueError: + raise SystemExit("Error while parsing {}".format(VMWARE_NETWORKING_FILE)) + except OSError as e: + raise SystemExit("Cannot open {}: {}".format(VMWARE_NETWORKING_FILE, e)) + return version, pairs, allocated_subnets + + +def write_networking_file(version, pairs): + """ + Write the VMware networking file. + """ + + vmnets = OrderedDict(sorted(pairs.items(), key=lambda t: t[0])) + try: + with open(VMWARE_NETWORKING_FILE, "w", encoding="utf-8") as f: + f.write(version) + for key, value in vmnets.items(): + f.write("answer {} {}\n".format(key, value)) + except OSError as e: + raise SystemExit("Cannot open {}: {}".format(VMWARE_NETWORKING_FILE, e)) + + # restart VMware networking service + os.system("vmware-networks --stop") + os.system("vmware-networks --start") + + +def parse_vmnet_range(start, end): + """ + Parse the vmnet range on the command line. + """ + + class Range(argparse.Action): + def __call__(self, parser, args, values, option_string=None): + if len(values) != 2: + raise argparse.ArgumentTypeError("vmnet range must consist of 2 numbers") + if not start <= values[0] or not values[1] <= end: + raise argparse.ArgumentTypeError("vmnet range must be between {} and {}".format(start, end)) + setattr(args, self.dest, values) + return Range + + +def main(): + """ + Entry point for the VMNET tool. + """ + + if not sys.platform.startswith("linux"): + raise SystemExit("This program can only be used on Linux") + + parser = argparse.ArgumentParser(description='%(prog)s add/remove vmnet interfaces') + parser.add_argument('-r', "--range", nargs='+', action=parse_vmnet_range(1, 255), + type=int, help="vmnet range to add (default is {} {})".format(DEFAULT_RANGE[0], DEFAULT_RANGE[1])) + parser.add_argument("-C", "--clean", action="store_true", help="remove all vmnets excepting vmnet1 and vmnet8") + parser.add_argument("-l", "--list", action="store_true", help="list all existing vmnets") + + try: + args = parser.parse_args() + except argparse.ArgumentTypeError as e: + raise SystemExit(e) + + vmnet_range = args.range if args.range is not None else DEFAULT_RANGE + if not os.access(VMWARE_NETWORKING_FILE, os.W_OK): + raise SystemExit("You must run this script as root") + + version, pairs, allocated_subnets = parse_networking_file() + + if args.list: + for vmnet_number in range(1, 256): + vmnet_name = "VNET_{}_VIRTUAL_ADAPTER".format(vmnet_number) + if vmnet_name in pairs: + print("vmnet{}".format(vmnet_number)) + return + + if args.clean: + # clean all vmnets but vmnet1 and vmnet8 + for key in pairs.copy().keys(): + if key.startswith("VNET_1_") or key.startswith("VNET_8_"): + continue + del pairs[key] + else: + for vmnet_number in range(vmnet_range[0], vmnet_range[1]): + vmnet_name = "VNET_{}_VIRTUAL_ADAPTER".format(vmnet_number) + if vmnet_name in pairs: + continue + allocated_subnet = None + for subnet in ipaddress.ip_network("172.16.0.0/16").subnets(prefixlen_diff=8): + subnet = str(subnet.network_address) + if subnet not in allocated_subnets: + allocated_subnet = subnet + allocated_subnets.append(allocated_subnet) + break + + if allocated_subnet is None: + print("Couldn't allocate a subnet for vmnet{}".format(vmnet_number)) + continue + + print("Adding vmnet{}...".format(vmnet_number)) + pairs["VNET_{}_HOSTONLY_NETMASK".format(vmnet_number)] = "255.255.255.0" + pairs["VNET_{}_HOSTONLY_SUBNET".format(vmnet_number)] = allocated_subnet + pairs["VNET_{}_VIRTUAL_ADAPTER".format(vmnet_number)] = "yes" + + write_networking_file(version, pairs) + +if __name__ == '__main__': + main() From b1d21c7c321e60f6dc1ac546ab73d6052609c982 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 4 Jun 2015 10:51:21 +0200 Subject: [PATCH 046/336] Drop python 3.3 from Travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f52a902e..9fd54b25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "3.3" - "3.4" before_install: From e63b9ff0e625727806fdf355be882b2aff3e82bf Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 5 Jun 2015 14:54:22 -0600 Subject: [PATCH 047/336] Option to drop nvram & disk files for IOS routers in order to save disk space. --- gns3server/modules/dynamips/__init__.py | 7 ++- gns3server/modules/dynamips/nodes/router.py | 50 +++++++++++++++++++++ gns3server/schemas/dynamips_vm.py | 12 +++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index a8bb51e2..2f9492a1 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -171,8 +171,11 @@ class Dynamips(BaseManager): files = glob.glob(os.path.join(project_dir, "*.ghost")) files += glob.glob(os.path.join(project_dir, "*_lock")) files += glob.glob(os.path.join(project_dir, "ilt_*")) - files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_*_rommon_vars")) - files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_*_ssa")) + files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i[0-9]*_rommon_vars")) + files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i[0-9]*_ssa")) + files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i[0-9]*_log.txt")) + files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i[0-9]*_rom")) + files += glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i[0-9]*_bootflash")) for file in files: try: log.debug("Deleting file {}".format(file)) diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index bdd3e053..0b9b2ba5 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -88,6 +88,7 @@ class Router(BaseVM): self._exec_area = 64 # 64 MB on other systems self._disk0 = 0 # Megabytes self._disk1 = 0 # Megabytes + self._auto_delete_disks = False self._aux = aux self._mac_addr = "" self._system_id = "FTX0945W0MY" # processor board ID in IOS @@ -146,6 +147,7 @@ class Router(BaseVM): "exec_area": self._exec_area, "disk0": self._disk0, "disk1": self._disk1, + "auto_delete_disks": self._auto_delete_disks, "console": self._console, "aux": self._aux, "mac_addr": self._mac_addr, @@ -183,6 +185,16 @@ class Router(BaseVM): @asyncio.coroutine def create(self): + # delete any previous file with same Dynamips identifier + project_dir = os.path.join(self.project.module_working_directory(self.manager.module_name.lower())) + for file in glob.glob(os.path.join(project_dir, "c[0-9][0-9][0-9][0-9]_i{}_*".format(self._dynamips_id))): + try: + log.debug("Deleting file {}".format(file)) + yield from wait_run_in_executor(os.remove, file) + except OSError as e: + log.warn("Could not delete file {}: {}".format(file, e)) + continue + if not self._hypervisor: module_workdir = self.project.module_working_directory(self.manager.module_name.lower()) self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir) @@ -357,6 +369,20 @@ class Router(BaseVM): pass yield from self.hypervisor.stop() + if self._auto_delete_disks: + # delete nvram and disk files + project_dir = os.path.join(self.project.module_working_directory(self.manager.module_name.lower())) + files = glob.glob(os.path.join(project_dir, "{}_i{}_disk[0-1]".format(self.platform, self.dynamips_id))) + files += glob.glob(os.path.join(project_dir, "{}_i{}_slot[0-1]".format(self.platform, self.dynamips_id))) + files += glob.glob(os.path.join(project_dir, "{}_i{}_nvram".format(self.platform, self.dynamips_id))) + files += glob.glob(os.path.join(project_dir, "{}_i{}_flash[0-1]".format(self.platform, self.dynamips_id))) + for file in files: + try: + log.debug("Deleting file {}".format(file)) + yield from wait_run_in_executor(os.remove, file) + except OSError as e: + log.warn("Could not delete file {}: {}".format(file, e)) + continue self._closed = True @property @@ -866,6 +892,30 @@ class Router(BaseVM): new_disk1=disk1)) self._disk1 = disk1 + @property + def auto_delete_disks(self): + """ + Returns True if auto delete disks is enabled on this router. + + :returns: boolean either auto delete disks is activated or not + """ + + return self._auto_delete_disks + + @asyncio.coroutine + def set_auto_delete_disks(self, auto_delete_disks): + """ + Enable/disable use of auto delete disks + + :param auto_delete_disks: activate/deactivate auto delete disks (boolean) + """ + + if auto_delete_disks: + log.info('Router "{name}" [{id}]: auto delete disks enabled'.format(name=self._name, id=self._id)) + else: + log.info('Router "{name}" [{id}]: auto delete disks disabled'.format(name=self._name, id=self._id)) + self._auto_delete_disks = auto_delete_disks + @asyncio.coroutine def set_console(self, console): """ diff --git a/gns3server/schemas/dynamips_vm.py b/gns3server/schemas/dynamips_vm.py index f505ac3c..4e6e9ee4 100644 --- a/gns3server/schemas/dynamips_vm.py +++ b/gns3server/schemas/dynamips_vm.py @@ -120,6 +120,10 @@ VM_CREATE_SCHEMA = { "description": "disk1 size in MB", "type": "integer" }, + "auto_delete_disks": { + "description": "automatically delete nvram and disk files", + "type": "boolean" + }, "console": { "description": "console TCP port", "type": "integer", @@ -337,6 +341,10 @@ VM_UPDATE_SCHEMA = { "description": "disk1 size in MB", "type": "integer" }, + "auto_delete_disks": { + "description": "automatically delete nvram and disk files", + "type": "boolean" + }, "console": { "description": "console TCP port", "type": "integer", @@ -591,6 +599,10 @@ VM_OBJECT_SCHEMA = { "description": "disk1 size in MB", "type": "integer" }, + "auto_delete_disks": { + "description": "automatically delete nvram and disk files", + "type": "boolean" + }, "console": { "description": "console TCP port", "type": "integer", From 1739acb67e717c909e641a17b1e7b27aeccd8c81 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Sat, 6 Jun 2015 13:42:25 +0200 Subject: [PATCH 048/336] Install qt5 for travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9fd54b25..126b783f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ before_install: - sudo add-apt-repository ppa:gns3/ppa -y - sudo apt-get update -q - sudo apt-get install dynamips + - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa + - sudo apt-get update -qq + - sudo apt-get install -qq qt5-qmake qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev install: - python setup.py install From ca331ae2a5efbe36ce1c7f3200adfbac40d0c3d1 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 6 Jun 2015 15:15:03 -0600 Subject: [PATCH 049/336] Import/Export support for IOU nvrams. --- gns3server/handlers/api/iou_handler.py | 69 ++++-- gns3server/modules/iou/__init__.py | 13 ++ gns3server/modules/iou/iou_vm.py | 234 +++++++++++++++++--- gns3server/modules/iou/utils/__init__.py | 0 gns3server/modules/iou/utils/iou_export.py | 239 ++++++++++++++++++++ gns3server/modules/iou/utils/iou_import.py | 241 +++++++++++++++++++++ gns3server/schemas/iou.py | 52 +++-- tests/handlers/api/test_iou.py | 46 ++-- tests/modules/iou/test_iou_vm.py | 44 ++-- tests/modules/qemu/test_qemu_vm.py | 3 +- utils/__init__.py | 1 - 11 files changed, 826 insertions(+), 116 deletions(-) create mode 100644 gns3server/modules/iou/utils/__init__.py create mode 100644 gns3server/modules/iou/utils/iou_export.py create mode 100644 gns3server/modules/iou/utils/iou_import.py diff --git a/gns3server/handlers/api/iou_handler.py b/gns3server/handlers/api/iou_handler.py index 520decc2..b4f8fe22 100644 --- a/gns3server/handlers/api/iou_handler.py +++ b/gns3server/handlers/api/iou_handler.py @@ -24,7 +24,7 @@ from ...schemas.iou import IOU_CREATE_SCHEMA from ...schemas.iou import IOU_UPDATE_SCHEMA from ...schemas.iou import IOU_OBJECT_SCHEMA from ...schemas.iou import IOU_CAPTURE_SCHEMA -from ...schemas.iou import IOU_INITIAL_CONFIG_SCHEMA +from ...schemas.iou import IOU_CONFIGS_SCHEMA from ...schemas.iou import IOU_LIST_VMS_SCHEMA from ...modules.iou import IOU @@ -59,11 +59,15 @@ class IOUHandler: for name, value in request.json.items(): if hasattr(vm, name) and getattr(vm, name) != value: - if name == "initial_config_content" and (vm.initial_config_content and len(vm.initial_config_content) > 0): + if name == "startup_config_content" and (vm.startup_config_content and len(vm.startup_config_content) > 0): + continue + if name == "private_config_content" and (vm.private_config_content and len(vm.private_config_content) > 0): continue setattr(vm, name, value) - if "initial_config_content" in request.json: - vm.initial_config = request.json.get("initial_config_content") + if "startup_config_content" in request.json: + vm.startup_config = request.json.get("startup_config_content") + if "private_config_content" in request.json: + vm.private_config = request.json.get("private_config_content") response.set_status(201) response.json(vm) @@ -111,8 +115,10 @@ class IOUHandler: for name, value in request.json.items(): if hasattr(vm, name) and getattr(vm, name) != value: setattr(vm, name, value) - if "initial_config_content" in request.json: - vm.initial_config = request.json.get("initial_config_content") + if "startup_config_content" in request.json: + vm.startup_config = request.json.get("startup_config_content") + if "private_config_content" in request.json: + vm.private_config = request.json.get("private_config_content") response.json(vm) @classmethod @@ -301,21 +307,56 @@ class IOUHandler: response.set_status(204) @Route.get( - r"/projects/{project_id}/iou/vms/{vm_id}/initial_config", + r"/projects/{project_id}/iou/vms/{vm_id}/configs", + status_codes={ + 200: "Configs retrieved", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + output=IOU_CONFIGS_SCHEMA, + description="Retrieve the startup and private configs content") + def get_configs(request, response): + + iou_manager = IOU.instance() + vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + + startup_config_content, private_config_content = vm.extract_configs() + result = {} + if startup_config_content: + result["startup_config_content"] = startup_config_content.decode("utf-8", errors='replace') + else: + # nvram doesn't exists if the VM has not been started at least once + # in this case just use the startup-config file + startup_config_content = vm.startup_config_content + if startup_config_content: + result["startup_config_content"] = startup_config_content + + if private_config_content: + result["private_config_content"] = private_config_content.decode("utf-8", errors='replace') + else: + # nvram doesn't exists if the VM has not been started at least once + # in this case just use the private-config file + private_config_content = vm.private_config_content + if private_config_content: + result["private_config_content"] = private_config_content + + response.set_status(200) + response.json(result) + + @Route.post( + r"/projects/{project_id}/iou/vms/{vm_id}/configs/save", status_codes={ - 200: "Initial config retrieved", + 200: "Configs saved", 400: "Invalid request", 404: "Instance doesn't exist" }, - output=IOU_INITIAL_CONFIG_SCHEMA, - description="Retrieve the initial config content") - def show_initial_config(request, response): + description="Save the startup and private configs content") + def save_configs(request, response): iou_manager = IOU.instance() - vm = iou_manager.get_vm(request.match_info["vm_id"], - project_id=request.match_info["project_id"]) + vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + vm.save_configs() response.set_status(200) - response.json({"content": vm.initial_config_content}) @Route.get( r"/iou/vms", diff --git a/gns3server/modules/iou/__init__.py b/gns3server/modules/iou/__init__.py index df6ad3c7..97bf678a 100644 --- a/gns3server/modules/iou/__init__.py +++ b/gns3server/modules/iou/__init__.py @@ -68,6 +68,19 @@ class IOU(BaseManager): yield from super().close_vm(vm_id, *args, **kwargs) return vm + @asyncio.coroutine + def project_committed(self, project): + """ + Called when a project has been committed. + + :param project: Project instance + """ + + # save the configs when the project is committed + for vm in self._vms.copy().values(): + if vm.project.id == project.id: + vm.save_configs() + def get_application_id(self, vm_id): """ Get an unique application identifier for IOU. diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index 590925c4..a025c9ad 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -33,6 +33,7 @@ import configparser import struct import hashlib import glob +import binascii from .iou_error import IOUError from ..adapters.ethernet_adapter import EthernetAdapter @@ -41,6 +42,8 @@ from ..nios.nio_udp import NIOUDP from ..nios.nio_tap import NIOTAP from ..nios.nio_generic_ethernet import NIOGenericEthernet from ..base_vm import BaseVM +from .utils.iou_import import nvram_import +from .utils.iou_export import nvram_export from .ioucon import start_ioucon import gns3server.utils.asyncio @@ -82,7 +85,8 @@ class IOUVM(BaseVM): self.serial_adapters = 2 # one adapter = 4 interfaces self._use_default_iou_values = True # for RAM & NVRAM values self._nvram = 128 # Kilobytes - self._initial_config = "" + self._startup_config = "" + self._private_config = "" self._ram = 256 # Megabytes self._l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes). @@ -106,6 +110,7 @@ class IOUVM(BaseVM): self.manager.port_manager.release_udp_port(nio.lport, self._project) yield from self.stop() + self.save_configs() @property def path(self): @@ -208,7 +213,8 @@ class IOUVM(BaseVM): "ram": self._ram, "nvram": self._nvram, "l1_keepalives": self._l1_keepalives, - "initial_config": self.relative_initial_config_file, + "startup_config": self.relative_startup_config_file, + "private_config": self.relative_private_config_file, "iourc_path": self.iourc_path, "use_default_iou_values": self._use_default_iou_values} @@ -316,10 +322,10 @@ class IOUVM(BaseVM): :param new_name: name """ - if self.initial_config_file: - content = self.initial_config_content + if self.startup_config_file: + content = self.startup_config_content content = content.replace(self._name, new_name) - self.initial_config_content = content + self.startup_config_content = content super(IOUVM, IOUVM).name.__set__(self, new_name) @@ -422,6 +428,36 @@ class IOUVM(BaseVM): self.iourc_path, hostname)) + def _push_configs_to_nvram(self): + """ + Push the startup-config and private-config content to the NVRAM. + """ + + startup_config_content = self.startup_config_content + if startup_config_content: + nvram_file = os.path.join(self.working_dir, "nvram_{:05d}".format(self.application_id)) + try: + if not os.path.exists(nvram_file): + open(nvram_file, "a").close() + with open(nvram_file, "rb") as file: + nvram_content = file.read() + except OSError as e: + raise IOUError("Cannot read nvram file {}: {}".format(nvram_file, e)) + + startup_config_content = startup_config_content.encode("utf-8") + private_config_content = self.private_config_content + if private_config_content is not None: + private_config_content = private_config_content.encode("utf-8") + try: + nvram_content = nvram_import(nvram_content, startup_config_content, private_config_content, self.nvram) + except ValueError as e: + raise IOUError("Cannot push configs to nvram {}: {}".format(nvram_file, e)) + try: + with open(nvram_file, "wb") as file: + file.write(nvram_content) + except OSError as e: + raise IOUError("Cannot write nvram file {}: {}".format(nvram_file, e)) + @asyncio.coroutine def start(self): """ @@ -450,6 +486,8 @@ class IOUVM(BaseVM): raise IOUError("iouyap is necessary to start IOU") self._create_netmap_config() + self._push_configs_to_nvram() + # created a environment variable pointing to the iourc file. env = os.environ.copy() @@ -747,9 +785,11 @@ class IOUVM(BaseVM): command.extend(["-m", str(self._ram)]) command.extend(["-L"]) # disable local console, use remote console - initial_config_file = self.initial_config_file - if initial_config_file: - command.extend(["-c", os.path.basename(initial_config_file)]) + # do not let IOU create the NVRAM anymore + #startup_config_file = self.startup_config_file + #if startup_config_file: + # command.extend(["-c", os.path.basename(startup_config_file)]) + if self._l1_keepalives: yield from self._enable_l1_keepalives(command) command.extend([str(self.application_id)]) @@ -962,12 +1002,55 @@ class IOUVM(BaseVM): log.warn("could not determine if layer 1 keepalive messages are supported by {}: {}".format(os.path.basename(self._path), e)) @property - def initial_config_content(self): + def startup_config_content(self): + """ + Returns the content of the current startup-config file. + """ + + config_file = self.startup_config_file + if config_file is None: + return None + + try: + with open(config_file, "rb") as f: + return f.read().decode("utf-8", errors="replace") + except OSError as e: + raise IOUError("Can't read startup-config file '{}': {}".format(config_file, e)) + + @startup_config_content.setter + def startup_config_content(self, startup_config): + """ + Update the startup config + + :param startup_config: content of the startup configuration file + """ + + try: + startup_config_path = os.path.join(self.working_dir, "startup-config.cfg") + + if startup_config is None: + startup_config = '' + + # We disallow erasing the startup config file + if len(startup_config) == 0 and os.path.exists(startup_config_path): + return + + with open(startup_config_path, 'w+', encoding='utf-8') as f: + if len(startup_config) == 0: + f.write('') + else: + startup_config = startup_config.replace("%h", self._name) + f.write(startup_config) + except OSError as e: + raise IOUError("Can't write startup-config file '{}': {}".format(startup_config_path, e)) + + @property + def private_config_content(self): """ - Returns the content of the current initial-config file. + Returns the content of the current private-config file. """ - config_file = self.initial_config_file + config_file = self.private_config_file if config_file is None: return None @@ -975,64 +1058,147 @@ class IOUVM(BaseVM): with open(config_file, "rb") as f: return f.read().decode("utf-8", errors="replace") except OSError as e: - raise IOUError("Can't read configuration file '{}': {}".format(config_file, e)) + raise IOUError("Can't read private-config file '{}': {}".format(config_file, e)) - @initial_config_content.setter - def initial_config_content(self, initial_config): + @private_config_content.setter + def private_config_content(self, private_config): """ - Update the initial config + Update the private config - :param initial_config: content of the initial configuration file + :param private_config: content of the private configuration file """ try: - initial_config_path = os.path.join(self.working_dir, "initial-config.cfg") + private_config_path = os.path.join(self.working_dir, "private-config.cfg") - if initial_config is None: - initial_config = '' + if private_config is None: + private_config = '' - # We disallow erasing the initial config file - if len(initial_config) == 0 and os.path.exists(initial_config_path): + # We disallow erasing the startup config file + if len(private_config) == 0 and os.path.exists(private_config_path): return - with open(initial_config_path, 'w+', encoding='utf-8') as f: - if len(initial_config) == 0: + with open(private_config_path, 'w+', encoding='utf-8') as f: + if len(private_config) == 0: f.write('') else: - initial_config = initial_config.replace("%h", self._name) - f.write(initial_config) + private_config = private_config.replace("%h", self._name) + f.write(private_config) except OSError as e: - raise IOUError("Can't write initial configuration file '{}': {}".format(initial_config_path, e)) + raise IOUError("Can't write private-config file '{}': {}".format(private_config_path, e)) @property - def initial_config_file(self): + def startup_config_file(self): """ - Returns the initial config file for this IOU VM. + Returns the startup-config file for this IOU VM. :returns: path to config file. None if the file doesn't exist """ - path = os.path.join(self.working_dir, 'initial-config.cfg') + path = os.path.join(self.working_dir, 'startup-config.cfg') if os.path.exists(path): return path else: return None @property - def relative_initial_config_file(self): + def private_config_file(self): """ - Returns the initial config file relative to the project directory. - It's compatible with pre 1.3 projects. + Returns the private-config file for this IOU VM. :returns: path to config file. None if the file doesn't exist """ - path = os.path.join(self.working_dir, 'initial-config.cfg') + path = os.path.join(self.working_dir, 'private-config.cfg') if os.path.exists(path): - return 'initial-config.cfg' + return path else: return None + @property + def relative_startup_config_file(self): + """ + Returns the startup-config file relative to the project directory. + It's compatible with pre 1.3 projects. + + :returns: path to startup-config file. None if the file doesn't exist + """ + + path = os.path.join(self.working_dir, 'startup-config.cfg') + if os.path.exists(path): + return 'startup-config.cfg' + else: + return None + + @property + def relative_private_config_file(self): + """ + Returns the private-config file relative to the project directory. + + :returns: path to private-config file. None if the file doesn't exist + """ + + path = os.path.join(self.working_dir, 'private-config.cfg') + if os.path.exists(path): + return 'private-config.cfg' + else: + return None + + def extract_configs(self): + """ + Gets the contents of the config files + startup-config and private-config from NVRAM. + + :returns: tuple (startup-config, private-config) + """ + + nvram_file = os.path.join(self.working_dir, "nvram_{:05d}".format(self.application_id)) + if not os.path.exists(nvram_file): + return None, None + try: + with open(nvram_file, "rb") as file: + nvram_content = file.read() + except OSError as e: + log.warning("Cannot read nvram file {}: {}".format(nvram_file, e)) + return None, None + + try: + startup_config_content, private_config_content = nvram_export(nvram_content) + except ValueError as e: + log.warning("Could not export configs from nvram file".format(nvram_file, e)) + return None, None + + return startup_config_content, private_config_content + + def save_configs(self): + """ + Saves the startup-config and private-config to files. + """ + + if self.startup_config_content or self.private_config_content: + startup_config_content, private_config_content = self.extract_configs() + if startup_config_content: + config_path = os.path.join(self.working_dir, "startup-config.cfg") + try: + config = startup_config_content.decode("utf-8", errors="replace") + config = "!\n" + config.replace("\r", "") + with open(config_path, "wb") as f: + log.info("saving startup-config to {}".format(config_path)) + f.write(config.encode("utf-8")) + except (binascii.Error, OSError) as e: + raise IOUError("Could not save the startup configuration {}: {}".format(config_path, e)) + + if private_config_content: + config_path = os.path.join(self.working_dir, "private-config.cfg") + try: + config = private_config_content.decode("utf-8", errors="replace") + config = "!\n" + config.replace("\r", "") + with open(config_path, "wb") as f: + log.info("saving private-config to {}".format(config_path)) + f.write(config.encode("utf-8")) + except (binascii.Error, OSError) as e: + raise IOUError("Could not save the private configuration {}: {}".format(config_path, e)) + @asyncio.coroutine def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"): """ diff --git a/gns3server/modules/iou/utils/__init__.py b/gns3server/modules/iou/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gns3server/modules/iou/utils/iou_export.py b/gns3server/modules/iou/utils/iou_export.py new file mode 100644 index 00000000..7ac4c614 --- /dev/null +++ b/gns3server/modules/iou/utils/iou_export.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# To use python v2.7 change the first line to: +#!/usr/bin/env python + +# Copyright (C) 2015 Bernhard Ehlers +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This utility is a stripped down version of dynamips' nvram_export, +# ported from C to Python, see https://github.com/GNS3/dynamips +# nvram_export is (c) 2013 Flávio J. Saraiva + +""" +iou_export exports startup/private configuration from IOU NVRAM file. + +usage: iou_export [-h] NVRAM startup-config [private-config] + +positional arguments: + NVRAM NVRAM file + startup-config startup configuration + private-config private configuration + +optional arguments: + -h, --help show this help message and exit +""" + +import argparse +import sys +from array import array + + +# Uncompress data in .Z file format. +# Ported from dynamips' fs_nvram.c to python +# Adapted from 7zip's ZDecoder.cpp, which is licensed under LGPL 2.1. +def uncompress_LZC(data): + LZC_NUM_BITS_MIN = 9 + LZC_NUM_BITS_MAX = 16 + + in_data = array('B', data) + in_len = len(in_data) + out_data = array('B') + + if in_len == 0: + return out_data.tostring() + if in_len < 3: + raise ValueError('invalid length') + if in_data[0] != 0x1F or in_data[1] != 0x9D: + raise ValueError('invalid header') + + maxbits = in_data[2] & 0x1F + numItems = 1 << maxbits + blockMode = (in_data[2] & 0x80) != 0 + if maxbits < LZC_NUM_BITS_MIN or maxbits > LZC_NUM_BITS_MAX: + raise ValueError('not supported') + + parents = array('H', [0] * numItems) + suffixes = array('B', [0] * numItems) + stack = array('B', [0] * numItems) + + in_pos = 3 + numBits = LZC_NUM_BITS_MIN + head = 256 + if blockMode: + head += 1 + + needPrev = 0 + bitPos = 0 + numBufBits = 0 + + parents[256] = 0 + suffixes[256] = 0 + + buf_extend = array('B', [0] * 3) + + while True: + # fill buffer, when empty + if numBufBits == bitPos: + buf_len = min(in_len - in_pos, numBits) + buf = in_data[in_pos:in_pos+buf_len] + buf_extend + numBufBits = buf_len << 3 + bitPos = 0 + in_pos += buf_len + + # extract next symbol + bytePos = bitPos >> 3 + symbol = buf[bytePos] | buf[bytePos + 1] << 8 | buf[bytePos + 2] << 16 + symbol >>= bitPos & 7 + symbol &= (1 << numBits) - 1 + bitPos += numBits + + # check for special conditions: end, bad data, re-initialize dictionary + if bitPos > numBufBits: + break + if symbol >= head: + raise ValueError('invalid data') + if blockMode and symbol == 256: + numBufBits = bitPos = 0 + numBits = LZC_NUM_BITS_MIN + head = 257 + needPrev = 0 + continue + + # convert symbol to string + cur = symbol + i = len(stack) + while cur >= 256: + i -= 1 + stack[i] = suffixes[cur] + cur = parents[cur] + i -= 1 + stack[i] = cur + if needPrev: + suffixes[head - 1] = cur + if symbol == head - 1: + stack[-1] = cur + out_data.extend(stack[i:]) + + # update parents, check for numBits change + if head < numItems: + needPrev = 1 + parents[head] = symbol + head += 1 + if head > (1 << numBits): + if numBits < maxbits: + numBufBits = bitPos = 0 + numBits += 1 + else: + needPrev = 0 + + return out_data.tostring() + + +# extract 16 bit unsigned int from data +def get_uint16(data, off): + return data[off] << 8 | data[off+1] + + +# extract 32 bit unsigned int from data +def get_uint32(data, off): + return data[off] << 24 | data[off+1] << 16 | data[off+2] << 8 | data[off+3] + + +# export IOU NVRAM +def nvram_export(nvram): + nvram = array('B', nvram) + + # extract startup config + offset = 0 + if len(nvram) < offset + 36: + raise ValueError('invalid length') + if get_uint16(nvram, offset + 0) != 0xABCD: + raise ValueError('no startup config') + format = get_uint16(nvram, offset + 2) + length = get_uint32(nvram, offset + 16) + offset += 36 + if len(nvram) < offset + length: + raise ValueError('invalid length') + startup = nvram[offset:offset+length].tostring() + + # compressed startup config + if format == 2: + try: + startup = uncompress_LZC(startup) + except ValueError as err: + raise ValueError('uncompress startup: ' + str(err)) + + offset += length + # alignment to multiple of 4 + offset = (offset+3) & ~3 + # check for additonal offset of 4 + if len(nvram) >= offset + 8 and \ + get_uint16(nvram, offset + 4) == 0xFEDC and \ + get_uint16(nvram, offset + 6) == 1: + offset += 4 + + # extract private config + private = None + if len(nvram) >= offset + 16 and get_uint16(nvram, offset + 0) == 0xFEDC: + length = get_uint32(nvram, offset + 12) + offset += 16 + if len(nvram) >= offset + length: + private = nvram[offset:offset+length].tostring() + + return (startup, private) + + +if __name__ == '__main__': + # Main program + + parser = argparse.ArgumentParser(description='%(prog)s exports startup/private configuration from IOU NVRAM file.') + parser.add_argument('nvram', metavar='NVRAM', + help='NVRAM file') + parser.add_argument('startup', metavar='startup-config', + help='startup configuration') + parser.add_argument('private', metavar='private-config', nargs='?', + help='private configuration') + args = parser.parse_args() + + try: + fd = open(args.nvram, 'rb') + nvram = fd.read() + fd.close() + except (IOError, OSError) as err: + sys.stderr.write("Error reading file: {}\n".format(err)) + sys.exit(1) + + try: + startup, private = nvram_export(nvram) + except ValueError as err: + sys.stderr.write("nvram_export: {}\n".format(err)) + sys.exit(3) + + try: + fd = open(args.startup, 'wb') + fd.write(startup) + fd.close() + if args.private is not None: + if private is None: + sys.stderr.write("Warning: No private config\n") + else: + fd = open(args.private, 'wb') + fd.write(private) + fd.close() + except (IOError, OSError) as err: + sys.stderr.write("Error writing file: {}\n".format(err)) + sys.exit(1) diff --git a/gns3server/modules/iou/utils/iou_import.py b/gns3server/modules/iou/utils/iou_import.py new file mode 100644 index 00000000..cb3396fe --- /dev/null +++ b/gns3server/modules/iou/utils/iou_import.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# To use python v2.7 change the first line to: +#!/usr/bin/env python + +# Copyright (C) 2015 Bernhard Ehlers +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +iou_import imports startup/private configuration into IOU NVRAM file. + +usage: iou_import [-h] [-c size] NVRAM startup-config [private-config] + +positional arguments: + NVRAM NVRAM file + startup-config startup configuration + private-config private configuration + +optional arguments: + -h, --help show this help message and exit + -c size, --create size + create NVRAM file, size in kByte +""" + +import argparse +import sys +from array import array + + +# extract 16 bit unsigned int from data +def get_uint16(data, off): + return data[off] << 8 | data[off+1] + + +# extract 32 bit unsigned int from data +def get_uint32(data, off): + return data[off] << 24 | data[off+1] << 16 | data[off+2] << 8 | data[off+3] + + +# insert 16 bit unsigned int into data +def put_uint16(data, off, value): + data[off] = (value >> 8) & 0xff + data[off+1] = value & 0xff + + +# insert 32 bit unsigned int into data +def put_uint32(data, off, value): + data[off] = (value >> 24) & 0xff + data[off+1] = (value >> 16) & 0xff + data[off+2] = (value >> 8) & 0xff + data[off+3] = value & 0xff + + +# calculate padding +def padding(off, ios): + pad = (4 - off % 4) % 4 # padding to alignment of 4 + if ios <= 0x0F00 and pad != 0: # add 4 if IOS <= 15.0 + pad += 4 + return pad + + +# update checksum +def checksum(data, start, end): + put_uint16(data, start + 4, 0) # set checksum to 0 + + chk = 0 + idx = start + while idx < end-1: + chk += get_uint16(data, idx) + idx += 2 + if idx < end: + chk += data[idx] << 8 + + while chk >> 16: + chk = (chk & 0xffff) + (chk >> 16) + + chk = chk ^ 0xffff + put_uint16(data, start + 4, chk) # set checksum + + +# import IOU NVRAM +def nvram_import(nvram, startup, private, size): + BASE_ADDRESS = 0x10000000 + DEFAULT_IOS = 0x0F04 # IOS 15.4 + + if size is None: + nvram = array('B', nvram) + else: + nvram = array('B', [0] * (size*1024)) + + # check nvram size + nvram_len = len(nvram) + if nvram_len < 8*1024 or nvram_len > 1024*1024 or nvram_len % 1024 != 0: + raise ValueError('invalid length') + nvram_len = nvram_len // 2 + + # get size of current config + config_len = 0 + ios = None + try: + if get_uint16(nvram, 0) == 0xABCD: + ios = get_uint16(nvram, 6) + config_len = 36 + get_uint32(nvram, 16) + config_len += padding(config_len, ios) + if get_uint16(nvram, config_len) == 0xFEDC: + config_len += 16 + get_uint32(nvram, config_len + 12) + except IndexError: + raise ValueError('unknown nvram format') + if config_len > nvram_len: + raise ValueError('unknown nvram format') + + # calculate max. config size + max_config = nvram_len - 2*1024 # reserve 2k for files + idx = max_config + empty_sector = array('B', [0] * 1024) + while True: + idx -= 1024 + if idx < config_len: + break + # if valid file header: + if get_uint16(nvram, idx+0) == 0xDCBA and \ + get_uint16(nvram, idx+4) < 8 and \ + get_uint16(nvram, idx+6) <= 992: + max_config = idx + elif nvram[idx:idx+1024] != empty_sector: + break + + # import startup config + startup = array('B', startup) + if ios is None: + # Target IOS version is unknown. As some IOU don't work nicely with + # the padding of a different version, the startup config is padded + # with '\n' to the alignment of 4. + ios = DEFAULT_IOS + startup.extend([ord('\n')] * ((4 - len(startup) % 4) % 4)) + new_nvram = array('B', [0] * 36) # startup hdr + put_uint16(new_nvram, 0, 0xABCD) # magic + put_uint16(new_nvram, 2, 1) # raw data + put_uint16(new_nvram, 6, ios) # IOS version + put_uint32(new_nvram, 8, BASE_ADDRESS+36) # start address + put_uint32(new_nvram, 12, BASE_ADDRESS+36 + len(startup)) # end address + put_uint32(new_nvram, 16, len(startup)) # length + new_nvram.extend(startup) + new_nvram.extend([0] * padding(len(new_nvram), ios)) + + # import private config + if private is None: + private = array('B') + else: + private = array('B', private) + offset = len(new_nvram) + new_nvram.extend([0] * 16) # private hdr + put_uint16(new_nvram, 0 + offset, 0xFEDC) # magic + put_uint16(new_nvram, 2 + offset, 1) # raw data + put_uint32(new_nvram, 4 + offset, + BASE_ADDRESS + offset + 16) # start address + put_uint32(new_nvram, 8 + offset, + BASE_ADDRESS + offset + 16 + len(private)) # end address + put_uint32(new_nvram, 12 + offset, len(private)) # length + new_nvram.extend(private) + + # add rest + if len(new_nvram) > max_config: + raise ValueError('NVRAM size too small') + new_nvram.extend([0] * (max_config - len(new_nvram))) + new_nvram.extend(nvram[max_config:]) + + checksum(new_nvram, 0, nvram_len) + + return new_nvram.tostring() + + +if __name__ == '__main__': + # Main program + + def check_size(string): + try: + value = int(string) + except ValueError: + raise argparse.ArgumentTypeError('invalid int value: ' + string) + if value < 8 or value > 1024: + raise argparse.ArgumentTypeError('size must be 8..1024') + return value + + parser = argparse.ArgumentParser(description='%(prog)s imports startup/private configuration into IOU NVRAM file.') + parser.add_argument('-c', '--create', metavar='size', type=check_size, + help='create NVRAM file, size in kByte') + parser.add_argument('nvram', metavar='NVRAM', + help='NVRAM file') + parser.add_argument('startup', metavar='startup-config', + help='startup configuration') + parser.add_argument('private', metavar='private-config', nargs='?', + help='private configuration') + args = parser.parse_args() + + try: + if args.create is None: + fd = open(args.nvram, 'rb') + nvram = fd.read() + fd.close() + else: + nvram = None + fd = open(args.startup, 'rb') + startup = fd.read() + fd.close() + if args.private is None: + private = None + else: + fd = open(args.private, 'rb') + private = fd.read() + fd.close() + except (IOError, OSError) as err: + sys.stderr.write("Error reading file: {}\n".format(err)) + sys.exit(1) + + try: + nvram = nvram_import(nvram, startup, private, args.create) + except ValueError as err: + sys.stderr.write("nvram_import: {}\n".format(err)) + sys.exit(3) + + try: + fd = open(args.nvram, 'wb') + fd.write(nvram) + fd.close() + except (IOError, OSError) as err: + sys.stderr.write("Error writing file: {}\n".format(err)) + sys.exit(1) diff --git a/gns3server/schemas/iou.py b/gns3server/schemas/iou.py index 208054fc..00ce5bfc 100644 --- a/gns3server/schemas/iou.py +++ b/gns3server/schemas/iou.py @@ -70,12 +70,20 @@ IOU_CREATE_SCHEMA = { "description": "Use default IOU values", "type": ["boolean", "null"] }, - "initial_config": { - "description": "Path to the initial configuration of IOU", + "startup_config": { + "description": "Path to the startup-config of IOU", "type": ["string", "null"] }, - "initial_config_content": { - "description": "Initial configuration of IOU", + "private_config": { + "description": "Path to the private-config of IOU", + "type": ["string", "null"] + }, + "startup_config_content": { + "description": "Startup-config of IOU", + "type": ["string", "null"] + }, + "private_config_content": { + "description": "Private-config of IOU", "type": ["string", "null"] }, "iourc_content": { @@ -127,8 +135,12 @@ IOU_UPDATE_SCHEMA = { "description": "Always up ethernet interface", "type": ["boolean", "null"] }, - "initial_config_content": { - "description": "Initial configuration of IOU", + "startup_config_content": { + "description": "Startup-config of IOU", + "type": ["string", "null"] + }, + "private_config_content": { + "description": "Private-config of IOU", "type": ["string", "null"] }, "use_default_iou_values": { @@ -197,8 +209,12 @@ IOU_OBJECT_SCHEMA = { "description": "Always up ethernet interface", "type": "boolean" }, - "initial_config": { - "description": "Path of the initial config content relative to project directory", + "startup_config": { + "description": "Path of the startup-config content relative to project directory", + "type": ["string", "null"] + }, + "private_config": { + "description": "Path of the private-config content relative to project directory", "type": ["string", "null"] }, "use_default_iou_values": { @@ -211,7 +227,8 @@ IOU_OBJECT_SCHEMA = { } }, "additionalProperties": False, - "required": ["name", "vm_id", "console", "project_id", "path", "serial_adapters", "ethernet_adapters", "ram", "nvram", "l1_keepalives", "initial_config", "use_default_iou_values"] + "required": ["name", "vm_id", "console", "project_id", "path", "serial_adapters", "ethernet_adapters", + "ram", "nvram", "l1_keepalives", "startup_config", "private_config", "use_default_iou_values"] } IOU_CAPTURE_SCHEMA = { @@ -234,18 +251,23 @@ IOU_CAPTURE_SCHEMA = { "required": ["capture_file_name", "data_link_type"] } -IOU_INITIAL_CONFIG_SCHEMA = { +IOU_CONFIGS_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to get the initial configuration file", + "description": "Request validation to get the startup and private configuration file", "type": "object", "properties": { - "content": { - "description": "Content of the initial configuration file", - "type": ["string", "null"] + "startup_config_content": { + "description": "Content of the startup configuration file", + "type": ["string", "null"], + "minLength": 1, + }, + "private_config_content": { + "description": "Content of the private configuration file", + "type": ["string", "null"], + "minLength": 1, }, }, "additionalProperties": False, - "required": ["content"] } IOU_LIST_VMS_SCHEMA = { diff --git a/tests/handlers/api/test_iou.py b/tests/handlers/api/test_iou.py index 962cd81d..743a0790 100644 --- a/tests/handlers/api/test_iou.py +++ b/tests/handlers/api/test_iou.py @@ -52,10 +52,10 @@ def vm(server, project, base_params): return response.json -def initial_config_file(project, vm): +def startup_config_file(project, vm): directory = os.path.join(project.path, "project-files", "iou", vm["vm_id"]) os.makedirs(directory, exist_ok=True) - return os.path.join(directory, "initial-config.cfg") + return os.path.join(directory, "startup-config.cfg") def test_iou_create(server, project, base_params): @@ -78,7 +78,7 @@ def test_iou_create_with_params(server, project, base_params): params["serial_adapters"] = 4 params["ethernet_adapters"] = 0 params["l1_keepalives"] = True - params["initial_config_content"] = "hostname test" + params["startup_config_content"] = "hostname test" params["use_default_iou_values"] = True params["iourc_content"] = "test" @@ -94,31 +94,31 @@ def test_iou_create_with_params(server, project, base_params): assert response.json["l1_keepalives"] is True assert response.json["use_default_iou_values"] is True - assert "initial-config.cfg" in response.json["initial_config"] - with open(initial_config_file(project, response.json)) as f: + assert "startup-config.cfg" in response.json["startup_config"] + with open(startup_config_file(project, response.json)) as f: assert f.read() == "hostname test" assert "iourc" in response.json["iourc_path"] -def test_iou_create_initial_config_already_exist(server, project, base_params): - """We don't erase an initial config if already exist at project creation""" +def test_iou_create_startup_config_already_exist(server, project, base_params): + """We don't erase a startup-config if already exist at project creation""" vm_id = str(uuid.uuid4()) - initial_config_file_path = initial_config_file(project, {'vm_id': vm_id}) - with open(initial_config_file_path, 'w+') as f: + startup_config_file_path = startup_config_file(project, {'vm_id': vm_id}) + with open(startup_config_file_path, 'w+') as f: f.write("echo hello") params = base_params params["vm_id"] = vm_id - params["initial_config_content"] = "hostname test" + params["startup_config_content"] = "hostname test" response = server.post("/projects/{project_id}/iou/vms".format(project_id=project.id), params, example=True) assert response.status == 201 assert response.route == "/projects/{project_id}/iou/vms" - assert "initial-config.cfg" in response.json["initial_config"] - with open(initial_config_file(project, response.json)) as f: + assert "startup-config.cfg" in response.json["startup_config"] + with open(startup_config_file(project, response.json)) as f: assert f.read() == "echo hello" @@ -172,7 +172,7 @@ def test_iou_update(server, vm, tmpdir, free_console_port, project): "ethernet_adapters": 4, "serial_adapters": 0, "l1_keepalives": True, - "initial_config_content": "hostname test", + "startup_config_content": "hostname test", "use_default_iou_values": True, "iourc_content": "test" } @@ -186,8 +186,8 @@ def test_iou_update(server, vm, tmpdir, free_console_port, project): assert response.json["nvram"] == 2048 assert response.json["l1_keepalives"] is True assert response.json["use_default_iou_values"] is True - assert "initial-config.cfg" in response.json["initial_config"] - with open(initial_config_file(project, response.json)) as f: + assert "startup-config.cfg" in response.json["startup_config"] + with open(startup_config_file(project, response.json)) as f: assert f.read() == "hostname test" assert "iourc" in response.json["iourc_path"] @@ -294,22 +294,22 @@ def test_iou_stop_capture_not_started(server, vm, tmpdir): assert response.status == 409 -def test_get_initial_config_without_config_file(server, vm): +def test_get_configs_without_configs_file(server, vm): - response = server.get("/projects/{project_id}/iou/vms/{vm_id}/initial_config".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True) + response = server.get("/projects/{project_id}/iou/vms/{vm_id}/configs".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True) assert response.status == 200 - assert response.json["content"] == None + assert "startup_config" not in response.json + assert "private_config" not in response.json +def test_get_configs_with_startup_config_file(server, project, vm): -def test_get_initial_config_with_config_file(server, project, vm): - - path = initial_config_file(project, vm) + path = startup_config_file(project, vm) with open(path, "w+") as f: f.write("TEST") - response = server.get("/projects/{project_id}/iou/vms/{vm_id}/initial_config".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True) + response = server.get("/projects/{project_id}/iou/vms/{vm_id}/configs".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True) assert response.status == 200 - assert response.json["content"] == "TEST" + assert response.json["startup_config_content"] == "TEST" def test_vms(server, tmpdir, fake_iou_bin): diff --git a/tests/modules/iou/test_iou_vm.py b/tests/modules/iou/test_iou_vm.py index fab4d48f..5028fd63 100644 --- a/tests/modules/iou/test_iou_vm.py +++ b/tests/modules/iou/test_iou_vm.py @@ -87,11 +87,11 @@ def test_vm(project, manager): assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" -def test_vm_initial_config_content(project, manager): +def test_vm_startup_config_content(project, manager): vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", project, manager) - vm.initial_config_content = "hostname %h" + vm.startup_config_content = "hostname %h" assert vm.name == "test" - assert vm.initial_config_content == "hostname test" + assert vm.startup_config_content == "hostname test" assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f" @@ -255,54 +255,44 @@ def test_build_command(vm, loop): assert loop.run_until_complete(asyncio.async(vm._build_command())) == [vm.path, "-L", str(vm.application_id)] - -def test_build_command_initial_config(vm, loop): - - filepath = os.path.join(vm.working_dir, "initial-config.cfg") - with open(filepath, "w+") as f: - f.write("service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption") - - assert loop.run_until_complete(asyncio.async(vm._build_command())) == [vm.path, "-L", "-c", os.path.basename(vm.initial_config_file), str(vm.application_id)] - - -def test_get_initial_config(vm): +def test_get_startup_config(vm): content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption" - vm.initial_config = content - assert vm.initial_config == content + vm.startup_config = content + assert vm.startup_config == content -def test_update_initial_config(vm): +def test_update_startup_config(vm): content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption" - vm.initial_config = content - filepath = os.path.join(vm.working_dir, "initial-config.cfg") + vm.startup_config_content = content + filepath = os.path.join(vm.working_dir, "startup-config.cfg") assert os.path.exists(filepath) with open(filepath) as f: assert f.read() == content -def test_update_initial_config_empty(vm): +def test_update_startup_config_empty(vm): content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption" - vm.initial_config = content - filepath = os.path.join(vm.working_dir, "initial-config.cfg") + vm.startup_config_content = content + filepath = os.path.join(vm.working_dir, "startup-config.cfg") assert os.path.exists(filepath) with open(filepath) as f: assert f.read() == content - vm.initial_config = "" + vm.startup_config_content = "" with open(filepath) as f: assert f.read() == content -def test_update_initial_config_content_hostname(vm): +def test_update_startup_config_content_hostname(vm): content = "hostname %h\n" vm.name = "pc1" - vm.initial_config_content = content - with open(vm.initial_config_file) as f: + vm.startup_config_content = content + with open(vm.startup_config_file) as f: assert f.read() == "hostname pc1\n" def test_change_name(vm, tmpdir): - path = os.path.join(vm.working_dir, "initial-config.cfg") + path = os.path.join(vm.working_dir, "startup-config.cfg") vm.name = "world" with open(path, 'w+') as f: f.write("hostname world") diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index ac939473..c150fd60 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -41,8 +41,7 @@ def manager(port_manager): @pytest.fixture def fake_qemu_img_binary(): - # Should not crash with unicode characters - bin_path = os.path.join(os.environ["PATH"], "qemu-img\u62FF") + bin_path = os.path.join(os.environ["PATH"], "qemu-img") with open(bin_path, "w+") as f: f.write("1") os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) diff --git a/utils/__init__.py b/utils/__init__.py index 0817d8f2..e69de29b 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1 +0,0 @@ -__author__ = 'grossmj' From 81e56e035b4c4e0ff724c940269011195014dae8 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 6 Jun 2015 21:37:34 -0600 Subject: [PATCH 050/336] IPv6 support. --- gns3server/modules/base_manager.py | 10 +++++++--- gns3server/modules/dynamips/__init__.py | 24 +++++++++++++++++------- gns3server/modules/iou/ioucon.py | 20 +++++++++++++------- gns3server/modules/port_manager.py | 14 +++++--------- gns3server/modules/qemu/qemu_vm.py | 13 +++++++++---- gns3server/utils/telnet_server.py | 11 +++++------ 6 files changed, 56 insertions(+), 36 deletions(-) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index 674eb419..9b3cc6da 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -357,9 +357,13 @@ class BaseManager: rhost = nio_settings["rhost"] rport = nio_settings["rport"] try: - # TODO: handle IPv6 - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: - sock.connect((rhost, rport)) + info = socket.getaddrinfo(rhost, rport, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE) + if not info: + raise aiohttp.web.HTTPInternalServerError(text="getaddrinfo returns an empty list on {}:{}".format(rhost, rport)) + for res in info: + af, socktype, proto, _, sa = res + with socket.socket(af, socktype, proto) as sock: + sock.connect(sa) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) nio = NIOUDP(lport, rhost, rport) diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 2f9492a1..f0658891 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -331,10 +331,16 @@ class Dynamips(BaseManager): server_host = server_config.get("host") try: - # let the OS find an unused port for the Dynamips hypervisor - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - sock.bind((server_host, 0)) - port = sock.getsockname()[1] + info = socket.getaddrinfo(server_host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) + if not info: + raise DynamipsError("getaddrinfo returns an empty list on {}".format(server_host)) + for res in info: + af, socktype, proto, _, sa = res + # let the OS find an unused port for the Dynamips hypervisor + with socket.socket(af, socktype, proto) as sock: + sock.bind(sa) + port = sock.getsockname()[1] + break except OSError as e: raise DynamipsError("Could not find free port for the Dynamips hypervisor: {}".format(e)) @@ -375,9 +381,13 @@ class Dynamips(BaseManager): rhost = nio_settings["rhost"] rport = nio_settings["rport"] try: - # TODO: handle IPv6 - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: - sock.connect((rhost, rport)) + info = socket.getaddrinfo(rhost, rport, 0, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE) + if not info: + raise DynamipsError("getaddrinfo returns an empty list on {}:{}".format(rhost, rport)) + for res in info: + af, socktype, proto, _, sa = res + with socket.socket(af, socktype, proto) as sock: + sock.connect(sa) except OSError as e: raise DynamipsError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) nio = NIOUDP(node.hypervisor, lport, rhost, rport) diff --git a/gns3server/modules/iou/ioucon.py b/gns3server/modules/iou/ioucon.py index 47c542ed..6f470843 100644 --- a/gns3server/modules/iou/ioucon.py +++ b/gns3server/modules/iou/ioucon.py @@ -355,13 +355,19 @@ class TelnetServer(Console): def __enter__(self): # Open a socket and start listening - sock_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - sock_fd.bind((self.addr, self.port)) - except OSError: - raise TelnetServerError("Cannot bind to {}:{}" - .format(self.addr, self.port)) + + info = socket.getaddrinfo(self.addr, self.port, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) + if not info: + raise TelnetServerError("getaddrinfo returns an empty list on {}:{}".format(self.addr, self.port)) + for res in info: + af, socktype, proto, _, sa = res + sock_fd = socket.socket(af, socktype, proto) + sock_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + sock_fd.bind(sa) + except OSError: + raise TelnetServerError("Cannot bind to {}:{}" + .format(self.addr, self.port)) sock_fd.listen(socket.SOMAXCONN) self.sock_fd = sock_fd diff --git a/gns3server/modules/port_manager.py b/gns3server/modules/port_manager.py index 0422e4ec..d0d3f7a0 100644 --- a/gns3server/modules/port_manager.py +++ b/gns3server/modules/port_manager.py @@ -151,16 +151,12 @@ class PortManager: if port in ignore_ports: continue try: - if ":" in host: - # IPv6 address support - with socket.socket(socket.AF_INET6, socket_type) as s: + for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type, 0, socket.AI_PASSIVE): + af, socktype, proto, _, sa = res + with socket.socket(af, socktype, proto) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((host, port)) # the port is available if bind is a success - else: - with socket.socket(socket.AF_INET, socket_type) as s: - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((host, port)) # the port is available if bind is a success - return port + s.bind(sa) # the port is available if bind is a success + return port except OSError as e: last_exception = e if port + 1 == end_port: diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 7d0a164d..f51dce34 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -615,10 +615,15 @@ class QemuVM(BaseVM): if self._manager.config.get_section_config("Qemu").getboolean("monitor", True): try: - # let the OS find an unused port for the Qemu monitor - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - sock.bind((self._monitor_host, 0)) - self._monitor = sock.getsockname()[1] + info = socket.getaddrinfo(self._monitor_host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) + if not info: + raise QemuError("getaddrinfo returns an empty list on {}".format(self._monitor_host)) + for res in info: + af, socktype, proto, _, sa = res + # let the OS find an unused port for the Qemu monitor + with socket.socket(af, socktype, proto) as sock: + sock.bind(sa) + self._monitor = sock.getsockname()[1] except OSError as e: raise QemuError("Could not find free port for the Qemu monitor: {}".format(e)) diff --git a/gns3server/utils/telnet_server.py b/gns3server/utils/telnet_server.py index a5d3ef4d..9bf7140e 100644 --- a/gns3server/utils/telnet_server.py +++ b/gns3server/utils/telnet_server.py @@ -60,14 +60,13 @@ class TelnetServer(threading.Thread): # we must a thread for reading the pipe on Windows because it is a Named Pipe and it cannot be monitored by select() self._use_thread = True - if ":" in self._host: - # IPv6 address support - self._server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - else: - self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): + af, socktype, proto, _, sa = res + self._server_socket = socket.socket(af, socktype, proto) self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self._server_socket.bind((self._host, self._port)) + self._server_socket.bind(sa) self._server_socket.listen(socket.SOMAXCONN) + break log.info("Telnet server initialized, waiting for clients on {}:{}".format(self._host, self._port)) From 81d417a2b35019f75e31d550f1a624fddfa6d4f7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 7 Jun 2015 09:25:54 -0600 Subject: [PATCH 051/336] Fixes too many arguments to socket.getaddrinfo() --- gns3server/modules/dynamips/__init__.py | 2 +- gns3server/modules/iou/ioucon.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index f0658891..b4910585 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -381,7 +381,7 @@ class Dynamips(BaseManager): rhost = nio_settings["rhost"] rport = nio_settings["rport"] try: - info = socket.getaddrinfo(rhost, rport, 0, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE) + info = socket.getaddrinfo(rhost, rport, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE) if not info: raise DynamipsError("getaddrinfo returns an empty list on {}:{}".format(rhost, rport)) for res in info: diff --git a/gns3server/modules/iou/ioucon.py b/gns3server/modules/iou/ioucon.py index 6f470843..7aeb0287 100644 --- a/gns3server/modules/iou/ioucon.py +++ b/gns3server/modules/iou/ioucon.py @@ -356,7 +356,7 @@ class TelnetServer(Console): def __enter__(self): # Open a socket and start listening - info = socket.getaddrinfo(self.addr, self.port, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) + info = socket.getaddrinfo(self.addr, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) if not info: raise TelnetServerError("getaddrinfo returns an empty list on {}:{}".format(self.addr, self.port)) for res in info: From 042472f02cec4e004685e5fec0d6fe46afa9c987 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 7 Jun 2015 13:51:33 -0600 Subject: [PATCH 052/336] Check interface is up before connecting a NIO (Linux only). Fixes #277. --- gns3server/modules/base_manager.py | 8 +++++++- gns3server/modules/dynamips/__init__.py | 6 +++++- gns3server/utils/interfaces.py | 27 +++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index 9b3cc6da..3ee0b347 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -28,6 +28,7 @@ import logging log = logging.getLogger(__name__) from uuid import UUID, uuid4 +from gns3server.utils.interfaces import is_interface_up from ..config import Config from ..utils.asyncio import wait_run_in_executor from .project_manager import ProjectManager @@ -369,12 +370,17 @@ class BaseManager: nio = NIOUDP(lport, rhost, rport) elif nio_settings["type"] == "nio_tap": tap_device = nio_settings["tap_device"] + if not is_interface_up(tap_device): + raise aiohttp.web.HTTPConflict(text="TAP interface {} is down".format(tap_device)) # FIXME: check for permissions on tap device # if not self._has_privileged_access(executable): # raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device)) nio = NIOTAP(tap_device) elif nio_settings["type"] == "nio_generic_ethernet": - nio = NIOGenericEthernet(nio_settings["ethernet_device"]) + ethernet_device = nio_settings["ethernet_device"] + if not is_interface_up(ethernet_device): + raise aiohttp.web.HTTPConflict(text="Ethernet interface {} is down".format(ethernet_device)) + nio = NIOGenericEthernet(ethernet_device) elif nio_settings["type"] == "nio_nat": nio = NIONAT() assert nio is not None diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index b4910585..d68bf5f5 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -32,7 +32,7 @@ import logging log = logging.getLogger(__name__) -from gns3server.utils.interfaces import get_windows_interfaces +from gns3server.utils.interfaces import get_windows_interfaces, is_interface_up from gns3server.utils.asyncio import wait_run_in_executor from pkg_resources import parse_version from uuid import UUID, uuid4 @@ -404,6 +404,8 @@ class Dynamips(BaseManager): raise DynamipsError("Could not find interface {} on this host".format(ethernet_device)) else: ethernet_device = npf_interface + if not is_interface_up(ethernet_device): + raise aiohttp.web.HTTPConflict(text="Ethernet interface {} is down".format(ethernet_device)) nio = NIOGenericEthernet(node.hypervisor, ethernet_device) elif nio_settings["type"] == "nio_linux_ethernet": if sys.platform.startswith("win"): @@ -412,6 +414,8 @@ class Dynamips(BaseManager): nio = NIOLinuxEthernet(node.hypervisor, ethernet_device) elif nio_settings["type"] == "nio_tap": tap_device = nio_settings["tap_device"] + if not is_interface_up(tap_device): + raise aiohttp.web.HTTPConflict(text="TAP interface {} is down".format(tap_device)) nio = NIOTAP(node.hypervisor, tap_device) elif nio_settings["type"] == "nio_unix": local_file = nio_settings["local_file"] diff --git a/gns3server/utils/interfaces.py b/gns3server/utils/interfaces.py index c9a5df88..60c16c61 100644 --- a/gns3server/utils/interfaces.py +++ b/gns3server/utils/interfaces.py @@ -18,6 +18,8 @@ import sys import aiohttp +import socket +import struct import logging log = logging.getLogger(__name__) @@ -78,6 +80,31 @@ def get_windows_interfaces(): return interfaces +def is_interface_up(interface): + """ + Checks if an interface is up. + + :param interface: interface name + + :returns: boolean + """ + + if sys.platform.startswith("linux"): + import fcntl + SIOCGIFFLAGS = 0x8913 + try: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + result = fcntl.ioctl(s.fileno(), SIOCGIFFLAGS, interface + '\0' * 256) + flags, = struct.unpack('H', result[16:18]) + if flags & 1: # check if the up bit is set + return True + return False + except OSError as e: + raise aiohttp.web.HTTPInternalServerError(text="Exception when checking if {} is up: {}".format(interface, e)) + else: + #TODO: Windows & OSX support + return True + def interfaces(): """ Gets the network interfaces on this server. From 65e7f61f96a24485a1794b62e5f83feae2958218 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 7 Jun 2015 22:18:41 -0600 Subject: [PATCH 053/336] Fixes issues when setting MAC address for a Qemu VM or IOS router. --- gns3server/modules/qemu/qemu_vm.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index f51dce34..bea80f74 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -75,7 +75,7 @@ class QemuVM(BaseVM): self._hdb_disk_image = "" self._hdc_disk_image = "" self._hdd_disk_image = "" - self._mac_address = "00:00:ab:%s:%s:00" % (self.id[-4:-2], self.id[-2:]) + self._mac_address = "" self._options = "" self._ram = 256 self._ethernet_adapters = [] @@ -88,6 +88,7 @@ class QemuVM(BaseVM): self._cpu_throttling = 0 # means no CPU throttling self._process_priority = "low" + self.mac_address = "" # this will generate a MAC address self.adapters = 1 # creates 1 adapter by default log.info('QEMU VM "{name}" [{id}] has been created'.format(name=self._name, id=self._id)) @@ -295,7 +296,10 @@ class QemuVM(BaseVM): :param mac_address: MAC address """ - self._mac_address = mac_address + if not mac_address: + self._mac_address = "00:00:ab:%s:%s:00" % (self.id[-4:-2], self.id[-2:]) + else: + self._mac_address = mac_address log.info('QEMU VM "{name}" [{id}]: MAC address changed to {mac_addr}'.format(name=self._name, id=self._id, From 815b7d226cdf177b925f577029a48b4e32efec2a Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 8 Jun 2015 10:07:54 -0600 Subject: [PATCH 054/336] Sync with IOU tools (nvram import/export). --- gns3server/modules/iou/iou_vm.py | 6 ++-- gns3server/modules/iou/utils/iou_export.py | 35 ++++++++++------------ gns3server/modules/iou/utils/iou_import.py | 26 +++++++++------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index a025c9ad..3c8bdaf4 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -439,8 +439,10 @@ class IOUVM(BaseVM): try: if not os.path.exists(nvram_file): open(nvram_file, "a").close() - with open(nvram_file, "rb") as file: - nvram_content = file.read() + nvram_content = None + else: + with open(nvram_file, "rb") as file: + nvram_content = file.read() except OSError as e: raise IOUError("Cannot read nvram file {}: {}".format(nvram_file, e)) diff --git a/gns3server/modules/iou/utils/iou_export.py b/gns3server/modules/iou/utils/iou_export.py index 7ac4c614..42dcd1f2 100644 --- a/gns3server/modules/iou/utils/iou_export.py +++ b/gns3server/modules/iou/utils/iou_export.py @@ -39,7 +39,6 @@ optional arguments: import argparse import sys -from array import array # Uncompress data in .Z file format. @@ -49,12 +48,12 @@ def uncompress_LZC(data): LZC_NUM_BITS_MIN = 9 LZC_NUM_BITS_MAX = 16 - in_data = array('B', data) + in_data = bytearray(data) in_len = len(in_data) - out_data = array('B') + out_data = bytearray() if in_len == 0: - return out_data.tostring() + return out_data if in_len < 3: raise ValueError('invalid length') if in_data[0] != 0x1F or in_data[1] != 0x9D: @@ -66,9 +65,8 @@ def uncompress_LZC(data): if maxbits < LZC_NUM_BITS_MIN or maxbits > LZC_NUM_BITS_MAX: raise ValueError('not supported') - parents = array('H', [0] * numItems) - suffixes = array('B', [0] * numItems) - stack = array('B', [0] * numItems) + parents = [0] * numItems + suffixes = [0] * numItems in_pos = 3 numBits = LZC_NUM_BITS_MIN @@ -83,7 +81,7 @@ def uncompress_LZC(data): parents[256] = 0 suffixes[256] = 0 - buf_extend = array('B', [0] * 3) + buf_extend = bytearray([0] * 3) while True: # fill buffer, when empty @@ -114,19 +112,18 @@ def uncompress_LZC(data): continue # convert symbol to string + stack = [] cur = symbol - i = len(stack) while cur >= 256: - i -= 1 - stack[i] = suffixes[cur] + stack.append(suffixes[cur]) cur = parents[cur] - i -= 1 - stack[i] = cur + stack.append(cur) if needPrev: suffixes[head - 1] = cur if symbol == head - 1: - stack[-1] = cur - out_data.extend(stack[i:]) + stack[0] = cur + stack.reverse() + out_data.extend(stack) # update parents, check for numBits change if head < numItems: @@ -140,7 +137,7 @@ def uncompress_LZC(data): else: needPrev = 0 - return out_data.tostring() + return out_data # extract 16 bit unsigned int from data @@ -155,7 +152,7 @@ def get_uint32(data, off): # export IOU NVRAM def nvram_export(nvram): - nvram = array('B', nvram) + nvram = bytearray(nvram) # extract startup config offset = 0 @@ -168,7 +165,7 @@ def nvram_export(nvram): offset += 36 if len(nvram) < offset + length: raise ValueError('invalid length') - startup = nvram[offset:offset+length].tostring() + startup = nvram[offset:offset+length] # compressed startup config if format == 2: @@ -192,7 +189,7 @@ def nvram_export(nvram): length = get_uint32(nvram, offset + 12) offset += 16 if len(nvram) >= offset + length: - private = nvram[offset:offset+length].tostring() + private = nvram[offset:offset+length] return (startup, private) diff --git a/gns3server/modules/iou/utils/iou_import.py b/gns3server/modules/iou/utils/iou_import.py index cb3396fe..5f54ad35 100644 --- a/gns3server/modules/iou/utils/iou_import.py +++ b/gns3server/modules/iou/utils/iou_import.py @@ -37,7 +37,6 @@ optional arguments: import argparse import sys -from array import array # extract 16 bit unsigned int from data @@ -96,15 +95,20 @@ def nvram_import(nvram, startup, private, size): BASE_ADDRESS = 0x10000000 DEFAULT_IOS = 0x0F04 # IOS 15.4 - if size is None: - nvram = array('B', nvram) + # check size parameter + if size is not None and (size < 8 or size > 1024): + raise ValueError('invalid size') + + # create new nvram if nvram is empty or has wrong size + if nvram is None or (size is not None and len(nvram) != size*1024): + nvram = bytearray([0] * (size*1024)) else: - nvram = array('B', [0] * (size*1024)) + nvram = bytearray(nvram) # check nvram size nvram_len = len(nvram) if nvram_len < 8*1024 or nvram_len > 1024*1024 or nvram_len % 1024 != 0: - raise ValueError('invalid length') + raise ValueError('invalid NVRAM length') nvram_len = nvram_len // 2 # get size of current config @@ -125,7 +129,7 @@ def nvram_import(nvram, startup, private, size): # calculate max. config size max_config = nvram_len - 2*1024 # reserve 2k for files idx = max_config - empty_sector = array('B', [0] * 1024) + empty_sector = bytearray([0] * 1024) while True: idx -= 1024 if idx < config_len: @@ -139,14 +143,14 @@ def nvram_import(nvram, startup, private, size): break # import startup config - startup = array('B', startup) + startup = bytearray(startup) if ios is None: # Target IOS version is unknown. As some IOU don't work nicely with # the padding of a different version, the startup config is padded # with '\n' to the alignment of 4. ios = DEFAULT_IOS startup.extend([ord('\n')] * ((4 - len(startup) % 4) % 4)) - new_nvram = array('B', [0] * 36) # startup hdr + new_nvram = bytearray([0] * 36) # startup hdr put_uint16(new_nvram, 0, 0xABCD) # magic put_uint16(new_nvram, 2, 1) # raw data put_uint16(new_nvram, 6, ios) # IOS version @@ -158,9 +162,9 @@ def nvram_import(nvram, startup, private, size): # import private config if private is None: - private = array('B') + private = bytearray() else: - private = array('B', private) + private = bytearray(private) offset = len(new_nvram) new_nvram.extend([0] * 16) # private hdr put_uint16(new_nvram, 0 + offset, 0xFEDC) # magic @@ -180,7 +184,7 @@ def nvram_import(nvram, startup, private, size): checksum(new_nvram, 0, nvram_len) - return new_nvram.tostring() + return new_nvram if __name__ == '__main__': From c7ea8517a1f1e6a22b4013ccd9e4a7ada40a8be9 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 8 Jun 2015 11:32:00 -0600 Subject: [PATCH 055/336] Do not stop saving IOS router or IOU configs when there is an exception while a project is committed. --- gns3server/modules/dynamips/__init__.py | 6 +++++- gns3server/modules/iou/__init__.py | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index d68bf5f5..86069668 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -213,7 +213,11 @@ class Dynamips(BaseManager): # save the configs when the project is committed for vm in self._vms.copy().values(): if vm.project.id == project.id: - yield from vm.save_configs() + try: + yield from vm.save_configs() + except DynamipsError as e: + log.warning(e) + continue @property def dynamips_path(self): diff --git a/gns3server/modules/iou/__init__.py b/gns3server/modules/iou/__init__.py index 97bf678a..92ffb526 100644 --- a/gns3server/modules/iou/__init__.py +++ b/gns3server/modules/iou/__init__.py @@ -26,6 +26,9 @@ from ..base_manager import BaseManager from .iou_error import IOUError from .iou_vm import IOUVM +import logging +log = logging.getLogger(__name__) + class IOU(BaseManager): @@ -79,7 +82,11 @@ class IOU(BaseManager): # save the configs when the project is committed for vm in self._vms.copy().values(): if vm.project.id == project.id: - vm.save_configs() + try: + vm.save_configs() + except IOUError as e: + log.warning(e) + continue def get_application_id(self, vm_id): """ From d518ac45f5b8a268101fd7ce4a7c8960b663fe02 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 8 Jun 2015 14:51:06 -0600 Subject: [PATCH 056/336] Enable KVM acceleration option. --- gns3server/modules/qemu/qemu_vm.py | 27 +++++++++++++++++++++++++++ gns3server/schemas/qemu.py | 14 +++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index bea80f74..69aea17e 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -84,6 +84,7 @@ class QemuVM(BaseVM): self._kernel_image = "" self._kernel_command_line = "" self._legacy_networking = False + self._kvm = True self._acpi_shutdown = False self._cpu_throttling = 0 # means no CPU throttling self._process_priority = "low" @@ -354,6 +355,30 @@ class QemuVM(BaseVM): log.info('QEMU VM "{name}" [{id}] has disabled ACPI shutdown'.format(name=self._name, id=self._id)) self._acpi_shutdown = acpi_shutdown + @property + def kvm(self): + """ + Returns either this QEMU VM uses KVM acceleration. + + :returns: boolean + """ + + return self._kvm + + @kvm.setter + def kvm(self, kvm): + """ + Sets either this QEMU VM uses KVM acceleration. + + :param kvm: boolean + """ + + if kvm: + log.info('QEMU VM "{name}" [{id}] has enabled KVM acceleration'.format(name=self._name, id=self._id)) + else: + log.info('QEMU VM "{name}" [{id}] has disabled KVM acceleration'.format(name=self._name, id=self._id)) + self._kvm = kvm + @property def cpu_throttling(self): """ @@ -1127,6 +1152,8 @@ class QemuVM(BaseVM): command = [self.qemu_path] command.extend(["-name", self._name]) command.extend(["-m", str(self._ram)]) + if sys.platform.startswith("linux") and self._kvm: + command.extend(["-enable-kvm"]) disk_options = yield from self._disk_options() command.extend(disk_options) command.extend(self._linux_boot_options()) diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index f4f97685..45a6dce7 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -104,6 +104,10 @@ QEMU_CREATE_SCHEMA = { "description": "ACPI shutdown support", "type": ["boolean", "null"], }, + "kvm": { + "description": "KVM support", + "type": ["boolean", "null"], + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -207,6 +211,10 @@ QEMU_UPDATE_SCHEMA = { "description": "ACPI shutdown support", "type": ["boolean", "null"], }, + "kvm": { + "description": "KVM support", + "type": ["boolean", "null"], + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -319,6 +327,10 @@ QEMU_OBJECT_SCHEMA = { "description": "ACPI shutdown support", "type": "boolean", }, + "kvm": { + "description": "KVM support", + "type": ["boolean", "null"], + }, "cpu_throttling": { "description": "Percentage of CPU allowed for QEMU", "minimum": 0, @@ -342,7 +354,7 @@ QEMU_OBJECT_SCHEMA = { "additionalProperties": False, "required": ["vm_id", "project_id", "name", "qemu_path", "hda_disk_image", "hdb_disk_image", "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "mac_address", "console", - "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "acpi_shutdown", + "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "acpi_shutdown", "kvm", "cpu_throttling", "process_priority", "options"] } From 669ec429851a6a5987c1752ecf07e77afd77e951 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 09:28:39 +0200 Subject: [PATCH 057/336] Add a test for KVM support --- tests/modules/qemu/test_qemu_vm.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index c150fd60..6b5ab538 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -278,6 +278,17 @@ def test_build_command(vm, loop, fake_qemu_binary, port_manager): ] +def test_build_command_with_kvm(vm, loop, fake_qemu_binary): + + vm.kvm = True + with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: + cmd = loop.run_until_complete(asyncio.async(vm._build_command())) + if sys.platform.startswith("linux"): + assert "-enable-kvm" in cmd + else: + assert "-enable-kvm" not in cmd + + @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_build_command_without_display(vm, loop, fake_qemu_binary): From 352fa3480bd8fc1d910b291916403a4d8ab7cf40 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 09:55:51 +0200 Subject: [PATCH 058/336] Fix qemu tests on travis --- tests/modules/qemu/test_qemu_vm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 6b5ab538..b03bb428 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -259,6 +259,7 @@ def test_control_vm_expect_text(vm, loop, running_subprocess_mock): def test_build_command(vm, loop, fake_qemu_binary, port_manager): os.environ["DISPLAY"] = "0:0" + vm.kvm = False with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: cmd = loop.run_until_complete(asyncio.async(vm._build_command())) assert cmd == [ From 525940ee1521ec3ff8f2179092fedcbf65eab5f6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 09:56:08 +0200 Subject: [PATCH 059/336] Add a stupid is_interface_up test in order to see why it's crash on travis Related to #230 --- tests/utils/test_interfaces.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/utils/test_interfaces.py b/tests/utils/test_interfaces.py index 9e899872..e712e4a3 100644 --- a/tests/utils/test_interfaces.py +++ b/tests/utils/test_interfaces.py @@ -16,9 +16,15 @@ # along with this program. If not, see . -from gns3server.utils.interfaces import interfaces +from gns3server.utils.interfaces import interfaces, is_interface_up def test_interfaces(): # This test should pass on all platforms without crash assert isinstance(interfaces(), list) + + +def test_is_interface_up(): + assert is_interface_up("eth0") is True + + From 36d5049c611e459ecf8ec7b3be03746f290adacd Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 10:10:06 +0200 Subject: [PATCH 060/336] More debug for the interfaces up crash debug --- .travis.yml | 1 + gns3server/utils/interfaces.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 126b783f..d2614c05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ install: - pip install -rdev-requirements.txt script: + - ifconfig - py.test -v -s tests --cov gns3server --cov-report term-missing #branches: diff --git a/gns3server/utils/interfaces.py b/gns3server/utils/interfaces.py index 60c16c61..85d6d1bd 100644 --- a/gns3server/utils/interfaces.py +++ b/gns3server/utils/interfaces.py @@ -100,11 +100,13 @@ def is_interface_up(interface): return True return False except OSError as e: - raise aiohttp.web.HTTPInternalServerError(text="Exception when checking if {} is up: {}".format(interface, e)) + raise e + #raise aiohttp.web.HTTPInternalServerError(text="Exception when checking if {} is up: {}".format(interface, e)) else: - #TODO: Windows & OSX support + # TODO: Windows & OSX support return True + def interfaces(): """ Gets the network interfaces on this server. From bd4b6396a8a8bfdfaa6ece2b51b2555bb3033ac5 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 16:25:36 +0200 Subject: [PATCH 061/336] Ensure in test we didn't load the local configuration --- gns3server/config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gns3server/config.py b/gns3server/config.py index e0086b5a..e1bae39f 100644 --- a/gns3server/config.py +++ b/gns3server/config.py @@ -45,7 +45,9 @@ class Config(object): # Monitor configuration files for changes self._watched_files = {} - if sys.platform.startswith("win"): + if hasattr(sys, "_called_from_test"): + self._files = files + elif sys.platform.startswith("win"): appname = "GNS3" @@ -87,6 +89,8 @@ class Config(object): os.path.join("/etc/xdg", appname + ".conf"), filename] + if self._files is None: + self._files = [] self.clear() self._watch_config_file() From 7c7f8fe3edec344f48ceb22544fe9c18c547bc26 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 16:35:21 +0200 Subject: [PATCH 062/336] Fix ethernet interfaces tests if eth0 doesn't exists Also on Windows and MacOS it return error if we try to use a non existing interfaces. Fix #230 --- gns3server/utils/interfaces.py | 17 ++++++++--------- tests/conftest.py | 6 ++++++ tests/handlers/api/test_iou.py | 16 ++++++++-------- tests/handlers/api/test_vpcs.py | 4 ++-- tests/modules/qemu/test_qemu_vm.py | 6 +++--- tests/modules/vpcs/test_vpcs_vm.py | 4 ++-- tests/utils/test_interfaces.py | 12 +++++++++--- 7 files changed, 38 insertions(+), 27 deletions(-) diff --git a/gns3server/utils/interfaces.py b/gns3server/utils/interfaces.py index 85d6d1bd..e9aa30b3 100644 --- a/gns3server/utils/interfaces.py +++ b/gns3server/utils/interfaces.py @@ -20,6 +20,7 @@ import sys import aiohttp import socket import struct +import netifaces import logging log = logging.getLogger(__name__) @@ -89,6 +90,9 @@ def is_interface_up(interface): :returns: boolean """ + if interface not in netifaces.interfaces(): + return False + if sys.platform.startswith("linux"): import fcntl SIOCGIFFLAGS = 0x8913 @@ -100,8 +104,7 @@ def is_interface_up(interface): return True return False except OSError as e: - raise e - #raise aiohttp.web.HTTPInternalServerError(text="Exception when checking if {} is up: {}".format(interface, e)) + raise aiohttp.web.HTTPInternalServerError(text="Exception when checking if {} is up: {}".format(interface, e)) else: # TODO: Windows & OSX support return True @@ -116,13 +119,9 @@ def interfaces(): results = [] if not sys.platform.startswith("win"): - try: - import netifaces - for interface in netifaces.interfaces(): - results.append({"id": interface, - "name": interface}) - except ImportError: - raise aiohttp.web.HTTPInternalServerError(text="Could not import netifaces module") + for interface in netifaces.interfaces(): + results.append({"id": interface, + "name": interface}) else: try: results = get_windows_interfaces() diff --git a/tests/conftest.py b/tests/conftest.py index 5e49a7da..9942e4ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -128,6 +128,12 @@ def free_console_port(request, port_manager, project): return port +@pytest.fixture +def ethernet_device(): + import netifaces + return netifaces.interfaces()[0] + + @pytest.yield_fixture(autouse=True) def run_around_tests(monkeypatch): """ diff --git a/tests/handlers/api/test_iou.py b/tests/handlers/api/test_iou.py index 743a0790..de6e0a22 100644 --- a/tests/handlers/api/test_iou.py +++ b/tests/handlers/api/test_iou.py @@ -204,32 +204,32 @@ def test_iou_nio_create_udp(server, vm): assert response.json["type"] == "nio_udp" -def test_iou_nio_create_ethernet(server, vm): +def test_iou_nio_create_ethernet(server, vm, ethernet_device): response = server.post("/projects/{project_id}/iou/vms/{vm_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_generic_ethernet", - "ethernet_device": "eth0", + "ethernet_device": ethernet_device, }, example=True) assert response.status == 201 assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.json["type"] == "nio_generic_ethernet" - assert response.json["ethernet_device"] == "eth0" + assert response.json["ethernet_device"] == ethernet_device -def test_iou_nio_create_ethernet_different_port(server, vm): +def test_iou_nio_create_ethernet_different_port(server, vm, ethernet_device): response = server.post("/projects/{project_id}/iou/vms/{vm_id}/adapters/0/ports/3/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_generic_ethernet", - "ethernet_device": "eth0", + "ethernet_device": ethernet_device, }, example=False) assert response.status == 201 assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.json["type"] == "nio_generic_ethernet" - assert response.json["ethernet_device"] == "eth0" + assert response.json["ethernet_device"] == ethernet_device -def test_iou_nio_create_tap(server, vm): +def test_iou_nio_create_tap(server, vm, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True): response = server.post("/projects/{project_id}/iou/vms/{vm_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_tap", - "tap_device": "test"}) + "tap_device": ethernet_device}) assert response.status == 201 assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.json["type"] == "nio_tap" diff --git a/tests/handlers/api/test_vpcs.py b/tests/handlers/api/test_vpcs.py index 736fb953..dc93d565 100644 --- a/tests/handlers/api/test_vpcs.py +++ b/tests/handlers/api/test_vpcs.py @@ -76,10 +76,10 @@ def test_vpcs_nio_create_udp(server, vm): assert response.json["type"] == "nio_udp" -def test_vpcs_nio_create_tap(server, vm): +def test_vpcs_nio_create_tap(server, vm, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True): response = server.post("/projects/{project_id}/vpcs/vms/{vm_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_tap", - "tap_device": "test"}) + "tap_device": ethernet_device}) assert response.status == 201 assert response.route == "/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.json["type"] == "nio_tap" diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index b03bb428..01b9b6f6 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -136,11 +136,11 @@ def test_add_nio_binding_udp(vm, loop): assert nio.lport == 4242 -def test_add_nio_binding_ethernet(vm, loop): +def test_add_nio_binding_ethernet(vm, loop, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True): - nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_generic_ethernet", "ethernet_device": "eth0"}) + nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_generic_ethernet", "ethernet_device": ethernet_device}) loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio))) - assert nio.ethernet_device == "eth0" + assert nio.ethernet_device == ethernet_device def test_port_remove_nio_binding(vm, loop): diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index 416147ee..3fb363f1 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -150,9 +150,9 @@ def test_add_nio_binding_udp(vm): assert nio.lport == 4242 -def test_add_nio_binding_tap(vm): +def test_add_nio_binding_tap(vm, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": "test"}) + nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": ethernet_device}) vm.port_add_nio_binding(0, nio) assert nio.tap_device == "test" diff --git a/tests/utils/test_interfaces.py b/tests/utils/test_interfaces.py index e712e4a3..401b0de1 100644 --- a/tests/utils/test_interfaces.py +++ b/tests/utils/test_interfaces.py @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import sys +import netifaces from gns3server.utils.interfaces import interfaces, is_interface_up @@ -25,6 +27,10 @@ def test_interfaces(): def test_is_interface_up(): - assert is_interface_up("eth0") is True - - + if sys.platform.startswith("win"): + assert is_interface_up(netifaces.interfaces[0]) is True + elif sys.platform.startswith("darwin"): + assert is_interface_up("lo0") is True + else: + assert is_interface_up("lo") is True + assert is_interface_up("fake0") is False From c10e692ca24e4141f7deba688f7b7bfbd312bff0 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Jun 2015 17:29:01 +0200 Subject: [PATCH 063/336] systemd start script and use netifaces from pip Fix #59, #229 --- README.rst | 18 ++++++++++++++++-- init/gns3.service.systemd | 14 ++++++++++++++ setup.py | 2 ++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 init/gns3.service.systemd diff --git a/README.rst b/README.rst index d8bb0b43..2ec6bcd1 100644 --- a/README.rst +++ b/README.rst @@ -42,7 +42,7 @@ You must be connected to the Internet in order to install the dependencies. Dependencies: -- Python 3.3 or above +- Python 3.4 or above - aiohttp - setuptools - netifaces @@ -53,7 +53,6 @@ The following commands will install some of these dependencies: .. code:: bash sudo apt-get install python3-setuptools - sudo apt-get install python3-netifaces Finally these commands will install the server as well as the rest of the dependencies: @@ -82,6 +81,12 @@ Usefull options: * --log logfile: store output in a logfile * --pid pidfile: store the pid of the running process in a file and prevent double execution +All the init script require the creation of a GNS3 user. You can change it to another user. + +.. code:: bash + + sudo adduser gns3 + upstart ~~~~~~~ @@ -95,6 +100,15 @@ You need to copy init/gns3.conf.upstart to /etc/init/gns3.conf sudo service gns3 start +systemd +~~~~~~~~ +You need to copy init/gns3.service.systemd to /lib/systemd/system/gns3.service + +.. code:: bash + + sudo chown root /lib/systemd/system/gns3.service + sudo + Windows ------- diff --git a/init/gns3.service.systemd b/init/gns3.service.systemd new file mode 100644 index 00000000..2a041a92 --- /dev/null +++ b/init/gns3.service.systemd @@ -0,0 +1,14 @@ +[Unit] +Description=GNS3 server + +[Service] +Type=forking +Environment=statedir=/var/cache/gns3 +PIDFile=/var/run/gns3.pid +ExecStart=/usr/local/bin/gns3server --log /var/log/gns3.log \ + --pid /var/run/gns3.pid --daemon +Restart=on-abort +User=gns3 + +[Install] +WantedBy=multi-user.target diff --git a/setup.py b/setup.py index 59a3d669..fb5ceaf4 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ from setuptools.command.test import test as TestCommand if sys.version_info < (3, 4): raise SystemExit("Python 3.4 or higher is required") + class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) @@ -38,6 +39,7 @@ class PyTest(TestCommand): dependencies = [ + "netifaces>=0.10.4", "jsonschema>=2.4.0", "aiohttp>=0.15.1", "Jinja2>=2.7.3", From b36c5f25d286571dc4fdead50c625afccb784c89 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 10 Jun 2015 14:20:06 +0200 Subject: [PATCH 064/336] Improve memory consumption of file upload with the HTML form Fix #86 --- gns3server/handlers/upload_handler.py | 7 +++++-- tests/handlers/test_upload.py | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gns3server/handlers/upload_handler.py b/gns3server/handlers/upload_handler.py index 9a431276..2ca39658 100644 --- a/gns3server/handlers/upload_handler.py +++ b/gns3server/handlers/upload_handler.py @@ -71,8 +71,11 @@ class UploadHandler: try: os.makedirs(destination_dir, exist_ok=True) with open(destination_path, "wb+") as f: - chunk = data["file"].file.read() - f.write(chunk) + while True: + chunk = data["file"].file.read(512) + if not chunk: + break + f.write(chunk) st = os.stat(destination_path) os.chmod(destination_path, st.st_mode | stat.S_IXUSR) except OSError as e: diff --git a/tests/handlers/test_upload.py b/tests/handlers/test_upload.py index 63d60ef8..0c1c2b1e 100644 --- a/tests/handlers/test_upload.py +++ b/tests/handlers/test_upload.py @@ -31,8 +31,10 @@ def test_index_upload(server): def test_upload(server, tmpdir): + content = ''.join(['a' for _ in range(0, 1025)]) + with open(str(tmpdir / "test"), "w+") as f: - f.write("TEST") + f.write(content) body = aiohttp.FormData() body.add_field("type", "QEMU") body.add_field("file", open(str(tmpdir / "test"), "rb"), content_type="application/iou", filename="test2") @@ -41,6 +43,6 @@ def test_upload(server, tmpdir): response = server.post('/upload', api_version=None, body=body, raw=True) with open(str(tmpdir / "QEMU" / "test2")) as f: - assert f.read() == "TEST" + assert f.read() == content assert "test2" in response.body.decode("utf-8") From b0fb2fcc6de59962570b19b0c25efb32e8f83fc3 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 10 Jun 2015 14:33:44 +0200 Subject: [PATCH 065/336] Fix test --- tests/modules/vpcs/test_vpcs_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index 3fb363f1..e0cb9ff0 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -154,7 +154,7 @@ def test_add_nio_binding_tap(vm, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True): nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": ethernet_device}) vm.port_add_nio_binding(0, nio) - assert nio.tap_device == "test" + assert nio.tap_device == ethernet_device # def test_add_nio_binding_tap_no_privileged_access(vm): From fb795433425f212858c3fe7c2beadcaf9f189f46 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 10 Jun 2015 14:35:53 +0200 Subject: [PATCH 066/336] Build documentation --- .../delete_projectsprojectidiouvmsvmid.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- .../delete_projectsprojectidqemuvmsvmid.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- .../delete_projectsprojectidvpcsvmsvmid.txt | 4 +- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- docs/api/examples/get_interfaces.txt | 8 ++-- docs/api/examples/get_iouvms.txt | 19 +++++++++ docs/api/examples/get_projectsprojectid.txt | 4 +- .../examples/get_projectsprojectidfiles.txt | 24 +++++++++++ .../get_projectsprojectidiouvmsvmid.txt | 13 +++--- ...get_projectsprojectidiouvmsvmidconfigs.txt | 17 ++++++++ ...ojectsprojectidiouvmsvmidinitialconfig.txt | 17 -------- .../get_projectsprojectidqemuvmsvmid.txt | 13 +++--- ...get_projectsprojectidvirtualboxvmsvmid.txt | 9 +++-- .../get_projectsprojectidvpcsvmsvmid.txt | 9 +++-- docs/api/examples/post_projects.txt | 6 +-- .../examples/post_projectsprojectidiouvms.txt | 17 ++++---- ...ptersadapternumberdportsportnumberdnio.txt | 10 ++--- ...ternumberdportsportnumberdstartcapture.txt | 6 +-- ...pternumberdportsportnumberdstopcapture.txt | 4 +- ...post_projectsprojectidiouvmsvmidreload.txt | 4 +- .../post_projectsprojectidiouvmsvmidstart.txt | 4 +- .../post_projectsprojectidiouvmsvmidstop.txt | 4 +- .../post_projectsprojectidqemuvms.txt | 13 +++--- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- ...ost_projectsprojectidqemuvmsvmidreload.txt | 4 +- ...ost_projectsprojectidqemuvmsvmidresume.txt | 4 +- ...post_projectsprojectidqemuvmsvmidstart.txt | 4 +- .../post_projectsprojectidqemuvmsvmidstop.txt | 4 +- ...st_projectsprojectidqemuvmsvmidsuspend.txt | 4 +- .../post_projectsprojectidvirtualboxvms.txt | 5 ++- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- ...ojectsprojectidvirtualboxvmsvmidreload.txt | 4 +- ...ojectsprojectidvirtualboxvmsvmidresume.txt | 4 +- ...rojectsprojectidvirtualboxvmsvmidstart.txt | 4 +- ...projectsprojectidvirtualboxvmsvmidstop.txt | 4 +- ...jectsprojectidvirtualboxvmsvmidsuspend.txt | 4 +- .../post_projectsprojectidvpcsvms.txt | 5 ++- ...ptersadapternumberdportsportnumberdnio.txt | 4 +- ...ost_projectsprojectidvpcsvmsvmidreload.txt | 4 +- ...post_projectsprojectidvpcsvmsvmidstart.txt | 4 +- .../post_projectsprojectidvpcsvmsvmidstop.txt | 4 +- docs/api/examples/put_projectsprojectid.txt | 6 +-- .../put_projectsprojectidiouvmsvmid.txt | 17 ++++---- .../put_projectsprojectidqemuvmsvmid.txt | 13 +++--- ...put_projectsprojectidvirtualboxvmsvmid.txt | 9 +++-- .../put_projectsprojectidvpcsvmsvmid.txt | 9 +++-- ...ojectsprojectiddynamipsdevicesdeviceid.rst | 6 +-- ...mipsdevicesdeviceidportsportnumberdnio.rst | 17 ++++++-- ...esdeviceidportsportnumberdstartcapture.rst | 2 +- ...cesdeviceidportsportnumberdstopcapture.rst | 2 +- .../v1/dynamips_vm/dynamipsvmsfilename.rst | 13 ++++++ .../projectsprojectiddynamipsvms.rst | 2 + .../projectsprojectiddynamipsvmsvmid.rst | 3 ++ ...ptersadapternumberdportsportnumberdnio.rst | 8 ++-- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- ...rojectsprojectiddynamipsvmsvmidconfigs.rst | 4 +- docs/api/v1/iou/iouvms.rst | 6 +++ docs/api/v1/iou/iouvmsfilename.rst | 13 ++++++ docs/api/v1/iou/projectsprojectidiouvms.rst | 9 +++-- .../v1/iou/projectsprojectidiouvmsvmid.rst | 9 +++-- ...ptersadapternumberdportsportnumberdnio.rst | 8 ++-- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- ...=> projectsprojectidiouvmsvmidconfigs.rst} | 13 +++--- ...projectsprojectidiouvmsvmidconfigssave.rst | 15 +++++++ .../api/v1/project/projectsprojectidfiles.rst | 24 +++++++++++ .../v1/project/projectsprojectidfilespath.rst | 19 +++++++++ .../projectsprojectidnotifications.rst | 18 +++++++++ docs/api/v1/qemu/projectsprojectidqemuvms.rst | 6 +++ .../v1/qemu/projectsprojectidqemuvmsvmid.rst | 9 +++++ ...ptersadapternumberdportsportnumberdnio.rst | 8 ++-- docs/api/v1/qemu/qemuimgbinaries.rst | 15 +++++++ docs/api/v1/qemu/qemuvmsfilename.rst | 13 ++++++ .../projectsprojectidvirtualboxvms.rst | 2 + .../projectsprojectidvirtualboxvmsvmid.rst | 3 ++ ...ptersadapternumberdportsportnumberdnio.rst | 8 ++-- ...ternumberdportsportnumberdstartcapture.rst | 4 +- ...pternumberdportsportnumberdstopcapture.rst | 4 +- .../v1/vmware/projectsprojectidvmwarevms.rst | 6 +++ .../vmware/projectsprojectidvmwarevmsvmid.rst | 9 +++++ ...ptersadapternumberdportsportnumberdnio.rst | 40 +++++++++++++++++++ docs/api/v1/vmware/vmwarevms.rst | 13 ++++++ docs/api/v1/vpcs/projectsprojectidvpcsvms.rst | 1 + .../v1/vpcs/projectsprojectidvpcsvmsvmid.rst | 2 + ...ptersadapternumberdportsportnumberdnio.rst | 8 ++-- tests/handlers/api/test_project.py | 2 +- tests/modules/vpcs/test_vpcs_vm.py | 2 +- 91 files changed, 515 insertions(+), 204 deletions(-) create mode 100644 docs/api/examples/get_iouvms.txt create mode 100644 docs/api/examples/get_projectsprojectidfiles.txt create mode 100644 docs/api/examples/get_projectsprojectidiouvmsvmidconfigs.txt delete mode 100644 docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt create mode 100644 docs/api/v1/dynamips_vm/dynamipsvmsfilename.rst create mode 100644 docs/api/v1/iou/iouvmsfilename.rst rename docs/api/v1/iou/{projectsprojectidiouvmsvmidinitialconfig.rst => projectsprojectidiouvmsvmidconfigs.rst} (52%) create mode 100644 docs/api/v1/iou/projectsprojectidiouvmsvmidconfigssave.rst create mode 100644 docs/api/v1/project/projectsprojectidfiles.rst create mode 100644 docs/api/v1/project/projectsprojectidfilespath.rst create mode 100644 docs/api/v1/project/projectsprojectidnotifications.rst create mode 100644 docs/api/v1/qemu/qemuimgbinaries.rst create mode 100644 docs/api/v1/qemu/qemuvmsfilename.rst create mode 100644 docs/api/v1/vmware/projectsprojectidvmwarevmsvmidadaptersadapternumberdportsportnumberdnio.rst create mode 100644 docs/api/v1/vmware/vmwarevms.rst diff --git a/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt b/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt index d1fa8cf3..053cd8f7 100644 --- a/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidiouvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/32773db2-f466-4118-8bef-02b585f92619' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2490ab98-57b1-4cb2-9214-6d741f5a5db3' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/32773db2-f466-4118-8bef-02b585f92619 HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2490ab98-57b1-4cb2-9214-6d741f5a5db3 HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt index eb8f1a08..ded37033 100644 --- a/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/22fb7e95-ac48-466a-a5e7-a03d2c9b57aa/adapters/1/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/4eb051c3-ac81-4768-8e26-d04a1dc3e540/adapters/1/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/22fb7e95-ac48-466a-a5e7-a03d2c9b57aa/adapters/1/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/4eb051c3-ac81-4768-8e26-d04a1dc3e540/adapters/1/ports/0/nio HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt index 84a4fcde..5beff400 100644 --- a/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidqemuvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/df85e698-3851-4172-b195-2caeb3fc5cf6' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/67edc289-2ece-4573-ab31-74eef86fc9ab' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/df85e698-3851-4172-b195-2caeb3fc5cf6 HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/67edc289-2ece-4573-ab31-74eef86fc9ab HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 89e38b92..9a61853f 100644 --- a/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2a5cfc20-5a1f-4180-897a-c90362a01832/adapters/1/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2f57c403-1fb8-44ad-91b9-1548b6679338/adapters/1/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2a5cfc20-5a1f-4180-897a-c90362a01832/adapters/1/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/2f57c403-1fb8-44ad-91b9-1548b6679338/adapters/1/ports/0/nio HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt index f67cc1d4..15bab244 100644 --- a/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c413ed57-0064-4e42-9cc0-59a6dc3143e0/adapters/0/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/ac6d84a3-c129-496e-88c9-c7dcf46f94fc/adapters/0/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c413ed57-0064-4e42-9cc0-59a6dc3143e0/adapters/0/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/ac6d84a3-c129-496e-88c9-c7dcf46f94fc/adapters/0/ports/0/nio HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt index b50c3a72..4a4fa5c7 100644 --- a/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/delete_projectsprojectidvpcsvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/44a0ec89-6cb1-4165-a4d5-ebf99963440f' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/8dd59ef3-c7f3-4cc5-a1f3-bf74dc10a417' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/44a0ec89-6cb1-4165-a4d5-ebf99963440f HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/8dd59ef3-c7f3-4cc5-a1f3-bf74dc10a417 HTTP/1.1 diff --git a/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 7a27263a..eafad1c2 100644 --- a/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/delete_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f12f73cc-dfd6-4b04-a731-1e0e6ead343a/adapters/0/ports/0/nio' +curl -i -X DELETE 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/6207942f-b820-4299-8b25-8897a671896d/adapters/0/ports/0/nio' -DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/f12f73cc-dfd6-4b04-a731-1e0e6ead343a/adapters/0/ports/0/nio HTTP/1.1 +DELETE /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/6207942f-b820-4299-8b25-8897a671896d/adapters/0/ports/0/nio HTTP/1.1 diff --git a/docs/api/examples/get_interfaces.txt b/docs/api/examples/get_interfaces.txt index d332fbe2..48851f13 100644 --- a/docs/api/examples/get_interfaces.txt +++ b/docs/api/examples/get_interfaces.txt @@ -29,6 +29,10 @@ X-ROUTE: /v1/interfaces "id": "en1", "name": "en1" }, + { + "id": "en0", + "name": "en0" + }, { "id": "fw0", "name": "fw0" @@ -37,10 +41,6 @@ X-ROUTE: /v1/interfaces "id": "en2", "name": "en2" }, - { - "id": "en0", - "name": "en0" - }, { "id": "p2p0", "name": "p2p0" diff --git a/docs/api/examples/get_iouvms.txt b/docs/api/examples/get_iouvms.txt new file mode 100644 index 00000000..2c14e9e2 --- /dev/null +++ b/docs/api/examples/get_iouvms.txt @@ -0,0 +1,19 @@ +curl -i -X GET 'http://localhost:8000/v1/iou/vms' + +GET /v1/iou/vms HTTP/1.1 + + + +HTTP/1.1 200 +CONNECTION: keep-alive +CONTENT-LENGTH: 45 +CONTENT-TYPE: application/json +DATE: Thu, 08 Jan 2015 16:09:15 GMT +SERVER: Python/3.4 GNS3/1.4.0.dev1 +X-ROUTE: /v1/iou/vms + +[ + { + "filename": "iou.bin" + } +] diff --git a/docs/api/examples/get_projectsprojectid.txt b/docs/api/examples/get_projectsprojectid.txt index c2fd7629..f6438b1c 100644 --- a/docs/api/examples/get_projectsprojectid.txt +++ b/docs/api/examples/get_projectsprojectid.txt @@ -13,9 +13,9 @@ SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id} { - "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpqyrnrqv5", + "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpgee6or3v", "name": "test", - "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpqyrnrqv5/00010203-0405-0607-0809-0a0b0c0d0e02", + "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpgee6or3v/00010203-0405-0607-0809-0a0b0c0d0e02", "project_id": "00010203-0405-0607-0809-0a0b0c0d0e02", "temporary": false } diff --git a/docs/api/examples/get_projectsprojectidfiles.txt b/docs/api/examples/get_projectsprojectidfiles.txt new file mode 100644 index 00000000..0ea9ee4b --- /dev/null +++ b/docs/api/examples/get_projectsprojectidfiles.txt @@ -0,0 +1,24 @@ +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/files' + +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/files HTTP/1.1 + + + +HTTP/1.1 200 +CONNECTION: keep-alive +CONTENT-LENGTH: 204 +CONTENT-TYPE: application/json +DATE: Thu, 08 Jan 2015 16:09:15 GMT +SERVER: Python/3.4 GNS3/1.4.0.dev1 +X-ROUTE: /v1/projects/{project_id}/files + +[ + { + "md5sum": "ad0234829205b9033196ba818f7a872b", + "path": "test.txt" + }, + { + "md5sum": "098f6bcd4621d373cade4e832627b4f6", + "path": "vm-1/dynamips/test.bin" + } +] diff --git a/docs/api/examples/get_projectsprojectidiouvmsvmid.txt b/docs/api/examples/get_projectsprojectidiouvmsvmid.txt index 169d6822..4d0a99b1 100644 --- a/docs/api/examples/get_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidiouvmsvmid.txt @@ -1,12 +1,12 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/570b3403-25ed-44bb-ab47-cbead7965a46' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/1f04d468-329c-44d1-b46f-9fa1a916aa70' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/570b3403-25ed-44bb-ab47-cbead7965a46 HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/1f04d468-329c-44d1-b46f-9fa1a916aa70 HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 467 +CONTENT-LENGTH: 495 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -15,15 +15,16 @@ X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} { "console": 2000, "ethernet_adapters": 2, - "initial_config": null, "iourc_path": null, "l1_keepalives": false, "name": "PC TEST 1", "nvram": 128, - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_get0/iou.bin", + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_iou_get0/iou.bin", + "private_config": null, "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 256, "serial_adapters": 2, + "startup_config": null, "use_default_iou_values": true, - "vm_id": "570b3403-25ed-44bb-ab47-cbead7965a46" + "vm_id": "1f04d468-329c-44d1-b46f-9fa1a916aa70" } diff --git a/docs/api/examples/get_projectsprojectidiouvmsvmidconfigs.txt b/docs/api/examples/get_projectsprojectidiouvmsvmidconfigs.txt new file mode 100644 index 00000000..7277b19a --- /dev/null +++ b/docs/api/examples/get_projectsprojectidiouvmsvmidconfigs.txt @@ -0,0 +1,17 @@ +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/053713a7-614a-488f-be8c-572f434d991b/configs' + +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/053713a7-614a-488f-be8c-572f434d991b/configs HTTP/1.1 + + + +HTTP/1.1 200 +CONNECTION: keep-alive +CONTENT-LENGTH: 40 +CONTENT-TYPE: application/json +DATE: Thu, 08 Jan 2015 16:09:15 GMT +SERVER: Python/3.4 GNS3/1.4.0.dev1 +X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/configs + +{ + "startup_config_content": "TEST" +} diff --git a/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt b/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt deleted file mode 100644 index fc6ef2c5..00000000 --- a/docs/api/examples/get_projectsprojectidiouvmsvmidinitialconfig.txt +++ /dev/null @@ -1,17 +0,0 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/733d8688-05fd-4e25-86fb-9ce302264936/initial_config' - -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/733d8688-05fd-4e25-86fb-9ce302264936/initial_config HTTP/1.1 - - - -HTTP/1.1 200 -CONNECTION: keep-alive -CONTENT-LENGTH: 25 -CONTENT-TYPE: application/json -DATE: Thu, 08 Jan 2015 16:09:15 GMT -SERVER: Python/3.4 GNS3/1.4.0.dev1 -X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/initial_config - -{ - "content": "TEST" -} diff --git a/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt index 0352bb8c..d8e53f19 100644 --- a/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidqemuvmsvmid.txt @@ -1,18 +1,19 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/86cb38a5-d580-41b2-ab31-57dd5c2e2317' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/5a34dba5-d6e4-4fd7-8ee0-63174bbb144a' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/86cb38a5-d580-41b2-ab31-57dd5c2e2317 HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/5a34dba5-d6e4-4fd7-8ee0-63174bbb144a HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 597 +CONTENT-LENGTH: 682 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} { + "acpi_shutdown": false, "adapter_type": "e1000", "adapters": 1, "console": 2000, @@ -24,12 +25,14 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} "initrd": "", "kernel_command_line": "", "kernel_image": "", + "kvm": true, "legacy_networking": false, + "mac_address": "00:00:ab:14:4a:00", "name": "PC TEST 1", "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp2vvnz4z_/qemu_x42", "ram": 256, - "vm_id": "86cb38a5-d580-41b2-ab31-57dd5c2e2317" + "vm_id": "5a34dba5-d6e4-4fd7-8ee0-63174bbb144a" } diff --git a/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt b/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt index 808686b4..48d1a354 100644 --- a/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidvirtualboxvmsvmid.txt @@ -1,18 +1,19 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c6006b62-b9c1-4be0-9cc6-81d12bdd29d5' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/e70eb611-1587-450c-a9af-6d10f33d73a6' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c6006b62-b9c1-4be0-9cc6-81d12bdd29d5 HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/e70eb611-1587-450c-a9af-6d10f33d73a6 HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 361 +CONTENT-LENGTH: 389 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} { + "acpi_shutdown": false, "adapter_type": "Intel PRO/1000 MT Desktop (82540EM)", "adapters": 0, "console": 2001, @@ -22,6 +23,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "c6006b62-b9c1-4be0-9cc6-81d12bdd29d5", + "vm_id": "e70eb611-1587-450c-a9af-6d10f33d73a6", "vmname": "VMTEST" } diff --git a/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt index 218c2d63..ac6e0131 100644 --- a/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/get_projectsprojectidvpcsvmsvmid.txt @@ -1,12 +1,12 @@ -curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e' +curl -i -X GET 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/bd6c3ed5-b279-4c51-bab2-4bd7a78fa6f7' -GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e HTTP/1.1 +GET /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/bd6c3ed5-b279-4c51-bab2-4bd7a78fa6f7 HTTP/1.1 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 220 +CONTENT-LENGTH: 245 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -18,5 +18,6 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": null, "startup_script_path": null, - "vm_id": "3d70ec8d-d4af-4bb3-9bc2-1e7833f44a3e" + "status": "stopped", + "vm_id": "bd6c3ed5-b279-4c51-bab2-4bd7a78fa6f7" } diff --git a/docs/api/examples/post_projects.txt b/docs/api/examples/post_projects.txt index 83c6f912..9db8647d 100644 --- a/docs/api/examples/post_projects.txt +++ b/docs/api/examples/post_projects.txt @@ -15,9 +15,9 @@ SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects { - "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpue5qtaa2", + "location": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpreddb0ft", "name": "test", - "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpue5qtaa2/abc875ab-6e67-484c-8db0-4e05de5f00b0", - "project_id": "abc875ab-6e67-484c-8db0-4e05de5f00b0", + "path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpreddb0ft/de3bdbbe-3f13-461b-9b1f-5fe764004670", + "project_id": "de3bdbbe-3f13-461b-9b1f-5fe764004670", "temporary": false } diff --git a/docs/api/examples/post_projectsprojectidiouvms.txt b/docs/api/examples/post_projectsprojectidiouvms.txt index 252cd4c3..f66aec29 100644 --- a/docs/api/examples/post_projectsprojectidiouvms.txt +++ b/docs/api/examples/post_projectsprojectidiouvms.txt @@ -1,17 +1,17 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms' -d '{"initial_config_content": "hostname test", "name": "PC TEST 1", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms' -d '{"name": "PC TEST 1", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_iou_create_startup_config0/iou.bin", "startup_config_content": "hostname test", "vm_id": "76cb4ec0-d88e-484b-ada6-4e5dd9c2cf74"}' POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms HTTP/1.1 { - "initial_config_content": "hostname test", "name": "PC TEST 1", - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", - "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899" + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_iou_create_startup_config0/iou.bin", + "startup_config_content": "hostname test", + "vm_id": "76cb4ec0-d88e-484b-ada6-4e5dd9c2cf74" } HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 501 +CONTENT-LENGTH: 529 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -20,15 +20,16 @@ X-ROUTE: /v1/projects/{project_id}/iou/vms { "console": 2000, "ethernet_adapters": 2, - "initial_config": "initial-config.cfg", "iourc_path": null, "l1_keepalives": false, "name": "PC TEST 1", "nvram": 128, - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_create_initial_config0/iou.bin", + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_iou_create_startup_config0/iou.bin", + "private_config": null, "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 256, "serial_adapters": 2, + "startup_config": "startup-config.cfg", "use_default_iou_values": true, - "vm_id": "aaf267e2-745e-40b3-bce7-3e80f5230899" + "vm_id": "76cb4ec0-d88e-484b-ada6-4e5dd9c2cf74" } diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt index c66434a6..dd9056d4 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,21 +1,21 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/5fff6292-2ac7-4f25-9fa7-5bdaaa7fbe84/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0f0f340a-7d25-4020-96f8-5d615437d173/adapters/1/ports/0/nio' -d '{"ethernet_device": "lo0", "type": "nio_generic_ethernet"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/5fff6292-2ac7-4f25-9fa7-5bdaaa7fbe84/adapters/1/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0f0f340a-7d25-4020-96f8-5d615437d173/adapters/1/ports/0/nio HTTP/1.1 { - "ethernet_device": "eth0", + "ethernet_device": "lo0", "type": "nio_generic_ethernet" } HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 69 +CONTENT-LENGTH: 68 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio { - "ethernet_device": "eth0", + "ethernet_device": "lo0", "type": "nio_generic_ethernet" } diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt index 1f1ad3bf..bbd8a16a 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3101e74a-8456-4ccf-ba7a-fd1b3cdf0729/adapters/0/ports/0/start_capture' -d '{"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/d5b26573-562d-4a4f-90a2-b8b6c8ce5b0f/adapters/0/ports/0/start_capture' -d '{"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/3101e74a-8456-4ccf-ba7a-fd1b3cdf0729/adapters/0/ports/0/start_capture HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/d5b26573-562d-4a4f-90a2-b8b6c8ce5b0f/adapters/0/ports/0/start_capture HTTP/1.1 { "capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB" @@ -16,5 +16,5 @@ SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture { - "pcap_file_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpossrvwje/a1e920ca-338a-4e9f-b363-aa607b09dd80/project-files/captures/test.pcap" + "pcap_file_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpfuqmuevw/a1e920ca-338a-4e9f-b363-aa607b09dd80/project-files/captures/test.pcap" } diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt index 0c85536f..8735004a 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2309d25b-8095-4a1a-9478-6345dad9f579/adapters/0/ports/0/stop_capture' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/4f23d204-379b-4403-a369-747315c67df6/adapters/0/ports/0/stop_capture' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2309d25b-8095-4a1a-9478-6345dad9f579/adapters/0/ports/0/stop_capture HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/4f23d204-379b-4403-a369-747315c67df6/adapters/0/ports/0/stop_capture HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt index 67c1ec57..56ba2a84 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/c4018290-fe62-41c8-bf06-88d832eb0477/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/ff11bb56-5bc4-4cca-8800-95c2d04b3d9e/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/c4018290-fe62-41c8-bf06-88d832eb0477/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/ff11bb56-5bc4-4cca-8800-95c2d04b3d9e/reload HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt index 6666f708..7d508404 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2fe292e5-ab46-473a-bd91-edf0ea58fc47/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0754f25c-e62c-4823-91a8-b7a7c81baee0/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/2fe292e5-ab46-473a-bd91-edf0ea58fc47/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/0754f25c-e62c-4823-91a8-b7a7c81baee0/start HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt index bee9429d..b8a8d115 100644 --- a/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidiouvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/8dc543e0-ae2c-45cc-bdc2-82baa781c8a7/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/bc841fee-2579-44fc-a32a-639fe3520590/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/8dc543e0-ae2c-45cc-bdc2-82baa781c8a7/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/bc841fee-2579-44fc-a32a-639fe3520590/stop HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidqemuvms.txt b/docs/api/examples/post_projectsprojectidqemuvms.txt index 74beb465..f8bcdb1f 100644 --- a/docs/api/examples/post_projectsprojectidqemuvms.txt +++ b/docs/api/examples/post_projectsprojectidqemuvms.txt @@ -1,23 +1,24 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms' -d '{"hda_disk_image": "/tmp/hda", "name": "PC TEST 1", "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", "ram": 1024}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms' -d '{"hda_disk_image": "/tmp/hda", "name": "PC TEST 1", "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp2vvnz4z_/qemu_x42", "ram": 1024}' POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms HTTP/1.1 { "hda_disk_image": "/tmp/hda", "name": "PC TEST 1", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp2vvnz4z_/qemu_x42", "ram": 1024 } HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 606 +CONTENT-LENGTH: 691 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms { + "acpi_shutdown": false, "adapter_type": "e1000", "adapters": 1, "console": 2000, @@ -29,12 +30,14 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms "initrd": "", "kernel_command_line": "", "kernel_image": "", + "kvm": true, "legacy_networking": false, + "mac_address": "00:00:ab:27:46:00", "name": "PC TEST 1", "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp2vvnz4z_/qemu_x42", "ram": 1024, - "vm_id": "c0fd0a60-1952-47e7-a293-7a233c83ce8e" + "vm_id": "3aabf5a2-3367-4863-afeb-4a3197d32746" } diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt index 1797d515..ab52e44f 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d378a8bd-711d-47b1-aaa0-dfa0c3ff420b/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/122f6e7b-5f24-4e22-9f3c-a62734c2a2c5/adapters/1/ports/0/nio' -d '{"ethernet_device": "eth0", "type": "nio_generic_ethernet"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d378a8bd-711d-47b1-aaa0-dfa0c3ff420b/adapters/1/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/122f6e7b-5f24-4e22-9f3c-a62734c2a2c5/adapters/1/ports/0/nio HTTP/1.1 { "ethernet_device": "eth0", "type": "nio_generic_ethernet" diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt index a80380b1..122e9a9b 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/96e58ba8-0d90-4f45-a410-ba738338b5f8/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8934f7c3-cfc2-4813-86e6-c431db2cc63a/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/96e58ba8-0d90-4f45-a410-ba738338b5f8/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8934f7c3-cfc2-4813-86e6-c431db2cc63a/reload HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt index ebd6e3c9..ddfd6ad1 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidresume.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/f112cd1e-d307-4f95-ab6b-8d2387d520d5/resume' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/167972e8-aa39-4894-9346-fca6db989043/resume' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/f112cd1e-d307-4f95-ab6b-8d2387d520d5/resume HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/167972e8-aa39-4894-9346-fca6db989043/resume HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt index d484e275..fa5e02cd 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/14c7e896-28cf-4705-801c-76ab2a4ee387/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/ea693a1a-e696-491e-9890-f685ea07d07a/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/14c7e896-28cf-4705-801c-76ab2a4ee387/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/ea693a1a-e696-491e-9890-f685ea07d07a/start HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt index 9cb889e1..45f2d660 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d730f891-a552-4df5-9399-fab1ed15358e/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/570da710-7809-43bd-bc09-f71e2e9ab7b0/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/d730f891-a552-4df5-9399-fab1ed15358e/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/570da710-7809-43bd-bc09-f71e2e9ab7b0/stop HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt b/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt index b45534fc..5de36b96 100644 --- a/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt +++ b/docs/api/examples/post_projectsprojectidqemuvmsvmidsuspend.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/64c8e5e5-056b-4ec8-9d86-912c7e053560/suspend' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/fd5203df-48c2-4185-b266-8bcf512dca51/suspend' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/64c8e5e5-056b-4ec8-9d86-912c7e053560/suspend HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/fd5203df-48c2-4185-b266-8bcf512dca51/suspend HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvms.txt b/docs/api/examples/post_projectsprojectidvirtualboxvms.txt index 1cd38b1d..72afb24a 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvms.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvms.txt @@ -10,13 +10,14 @@ POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms HTTP/1.1 HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 355 +CONTENT-LENGTH: 383 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms { + "acpi_shutdown": false, "adapter_type": "Intel PRO/1000 MT Desktop (82540EM)", "adapters": 0, "console": 2000, @@ -26,6 +27,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "f9407c6b-39bf-4445-9d53-70676fe7e3dc", + "vm_id": "de50008a-0f46-481b-99c0-9788618faa06", "vmname": "VM1" } diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt index fa91f290..b77abb6a 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/d18556e2-fc68-486c-a039-0cd9200322d3/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c8671922-23e0-43c7-bb36-dfb4d5fa82c9/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/d18556e2-fc68-486c-a039-0cd9200322d3/adapters/0/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/c8671922-23e0-43c7-bb36-dfb4d5fa82c9/adapters/0/ports/0/nio HTTP/1.1 { "lport": 4242, "rhost": "127.0.0.1", diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt index 4004050e..20be26eb 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/cb66c1db-e2f8-4149-9065-976db1af7afb/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f31ce37d-9ef3-45c8-8b3e-e6ad163aba9c/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/cb66c1db-e2f8-4149-9065-976db1af7afb/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/f31ce37d-9ef3-45c8-8b3e-e6ad163aba9c/reload HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt index cc304ddd..aab8e545 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidresume.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/158053af-713e-4a72-a05d-c33aa05208f7/resume' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/554fc4ca-6861-496f-a70b-f107c5423176/resume' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/158053af-713e-4a72-a05d-c33aa05208f7/resume HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/554fc4ca-6861-496f-a70b-f107c5423176/resume HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt index 34ae9b83..0630bc8d 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/a18cc3a2-a31b-4d4a-b79a-0b1e714760fb/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/1c59f62c-6728-43ae-a819-9506450f4bda/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/a18cc3a2-a31b-4d4a-b79a-0b1e714760fb/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/1c59f62c-6728-43ae-a819-9506450f4bda/start HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt index 41397c9e..84a7ff9f 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b897ee5-9c9f-4de1-9858-2a0280d2e551/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/980d0c2f-2c8d-495d-ab62-3ffffe899c65/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b897ee5-9c9f-4de1-9858-2a0280d2e551/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/980d0c2f-2c8d-495d-ab62-3ffffe899c65/stop HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt index f9cab5fe..9511817e 100644 --- a/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt +++ b/docs/api/examples/post_projectsprojectidvirtualboxvmsvmidsuspend.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b5139c6-3bcd-4edc-a37f-fd73d28cd9bd/suspend' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/afbc681b-8a60-4911-bacc-cc8d0b8f36bf/suspend' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/6b5139c6-3bcd-4edc-a37f-fd73d28cd9bd/suspend HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/afbc681b-8a60-4911-bacc-cc8d0b8f36bf/suspend HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvpcsvms.txt b/docs/api/examples/post_projectsprojectidvpcsvms.txt index 5d9f94ad..59e89e44 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvms.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvms.txt @@ -8,7 +8,7 @@ POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms HTTP/1.1 HTTP/1.1 201 CONNECTION: keep-alive -CONTENT-LENGTH: 220 +CONTENT-LENGTH: 245 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -20,5 +20,6 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": null, "startup_script_path": null, - "vm_id": "104b72e1-1d73-4153-989f-96731442f6a1" + "status": "stopped", + "vm_id": "a0b0ad7c-bffe-4fb1-8b85-b50016acc46a" } diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt index fa73145e..f78bc169 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/b5d32250-bacf-4594-be35-e2d2d814a45c/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/08dc7b7e-dc5a-44cc-bc80-8e71ce7c5973/adapters/0/ports/0/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/b5d32250-bacf-4594-be35-e2d2d814a45c/adapters/0/ports/0/nio HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/08dc7b7e-dc5a-44cc-bc80-8e71ce7c5973/adapters/0/ports/0/nio HTTP/1.1 { "lport": 4242, "rhost": "127.0.0.1", diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt index edc0e24e..1ede2bd1 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidreload.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/722b7e9d-ecc1-44cf-b743-609bb267ec65/reload' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/34458b84-4696-4914-8e38-5548f7783b50/reload' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/722b7e9d-ecc1-44cf-b743-609bb267ec65/reload HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/34458b84-4696-4914-8e38-5548f7783b50/reload HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt index ba660775..c1591d17 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstart.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/0986e25f-6d7e-4a64-a8ea-b1a3a5541c46/start' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/88a924fe-d7be-4e4f-bbe6-d01844ed6caa/start' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/0986e25f-6d7e-4a64-a8ea-b1a3a5541c46/start HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/88a924fe-d7be-4e4f-bbe6-d01844ed6caa/start HTTP/1.1 {} diff --git a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt index 2271f66c..24ce7a5b 100644 --- a/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt +++ b/docs/api/examples/post_projectsprojectidvpcsvmsvmidstop.txt @@ -1,6 +1,6 @@ -curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/a9b859ce-d847-402c-9db2-cd2d1bc4f67a/stop' -d '{}' +curl -i -X POST 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/249fb5b4-1b5c-4675-982c-375ad8f31fad/stop' -d '{}' -POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/a9b859ce-d847-402c-9db2-cd2d1bc4f67a/stop HTTP/1.1 +POST /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/249fb5b4-1b5c-4675-982c-375ad8f31fad/stop HTTP/1.1 {} diff --git a/docs/api/examples/put_projectsprojectid.txt b/docs/api/examples/put_projectsprojectid.txt index 13f3f6a7..15243116 100644 --- a/docs/api/examples/put_projectsprojectid.txt +++ b/docs/api/examples/put_projectsprojectid.txt @@ -1,9 +1,9 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/ac4b48cf-f26c-4ab6-bce1-368fdffb112f' -d '{"name": "second_name", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_update_path_project_non_l0"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/3df5ad8b-1d2a-4204-b8b2-3c14048f765c' -d '{"name": "second_name", "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_update_path_project_non_l0"}' -PUT /v1/projects/ac4b48cf-f26c-4ab6-bce1-368fdffb112f HTTP/1.1 +PUT /v1/projects/3df5ad8b-1d2a-4204-b8b2-3c14048f765c HTTP/1.1 { "name": "second_name", - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_update_path_project_non_l0" + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_update_path_project_non_l0" } diff --git a/docs/api/examples/put_projectsprojectidiouvmsvmid.txt b/docs/api/examples/put_projectsprojectidiouvmsvmid.txt index fc1bc621..dbe97ccf 100644 --- a/docs/api/examples/put_projectsprojectidiouvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidiouvmsvmid.txt @@ -1,23 +1,23 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b75e3ec5-b42d-4e42-80a3-e79b2aab1efa' -d '{"console": 2001, "ethernet_adapters": 4, "initial_config_content": "hostname test", "iourc_content": "test", "l1_keepalives": true, "name": "test", "nvram": 2048, "ram": 512, "serial_adapters": 0, "use_default_iou_values": true}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/65baee88-6b5f-4f4c-94d4-a4b37b74e701' -d '{"console": 2001, "ethernet_adapters": 4, "iourc_content": "test", "l1_keepalives": true, "name": "test", "nvram": 2048, "ram": 512, "serial_adapters": 0, "startup_config_content": "hostname test", "use_default_iou_values": true}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/b75e3ec5-b42d-4e42-80a3-e79b2aab1efa HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/iou/vms/65baee88-6b5f-4f4c-94d4-a4b37b74e701 HTTP/1.1 { "console": 2001, "ethernet_adapters": 4, - "initial_config_content": "hostname test", "iourc_content": "test", "l1_keepalives": true, "name": "test", "nvram": 2048, "ram": 512, "serial_adapters": 0, + "startup_config_content": "hostname test", "use_default_iou_values": true } HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 545 +CONTENT-LENGTH: 573 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -26,15 +26,16 @@ X-ROUTE: /v1/projects/{project_id}/iou/vms/{vm_id} { "console": 2001, "ethernet_adapters": 4, - "initial_config": "initial-config.cfg", - "iourc_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmprshqaaaj/iourc", + "iourc_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpbum1ehij/iourc", "l1_keepalives": true, "name": "test", "nvram": 2048, - "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-25/test_iou_update0/iou.bin", + "path": "/private/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/pytest-10/test_iou_update0/iou.bin", + "private_config": null, "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 512, "serial_adapters": 0, + "startup_config": "startup-config.cfg", "use_default_iou_values": true, - "vm_id": "b75e3ec5-b42d-4e42-80a3-e79b2aab1efa" + "vm_id": "65baee88-6b5f-4f4c-94d4-a4b37b74e701" } diff --git a/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt b/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt index 43bfd71f..38473fa1 100644 --- a/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidqemuvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8338f125-5b24-4bc9-a891-6ca1510396ad' -d '{"console": 2001, "hdb_disk_image": "/tmp/hdb", "name": "test", "ram": 1024}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/552a08c8-811f-4a20-97f7-8ada2a36f0a5' -d '{"console": 2001, "hdb_disk_image": "/tmp/hdb", "name": "test", "ram": 1024}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8338f125-5b24-4bc9-a891-6ca1510396ad HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/552a08c8-811f-4a20-97f7-8ada2a36f0a5 HTTP/1.1 { "console": 2001, "hdb_disk_image": "/tmp/hdb", @@ -11,13 +11,14 @@ PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/qemu/vms/8338f125-5b24-4bc HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 601 +CONTENT-LENGTH: 686 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} { + "acpi_shutdown": false, "adapter_type": "e1000", "adapters": 1, "console": 2001, @@ -29,12 +30,14 @@ X-ROUTE: /v1/projects/{project_id}/qemu/vms/{vm_id} "initrd": "", "kernel_command_line": "", "kernel_image": "", + "kvm": true, "legacy_networking": false, + "mac_address": "00:00:ab:f0:a5:00", "name": "test", "options": "", "process_priority": "low", "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", - "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmpnb5_wdn4/qemu_x42", + "qemu_path": "/var/folders/3s/r2wbv07n7wg4vrsn874lmxxh0000gn/T/tmp2vvnz4z_/qemu_x42", "ram": 1024, - "vm_id": "8338f125-5b24-4bc9-a891-6ca1510396ad" + "vm_id": "552a08c8-811f-4a20-97f7-8ada2a36f0a5" } diff --git a/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt b/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt index b4070901..60e4d5aa 100644 --- a/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidvirtualboxvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/4dac8148-a8d2-42a5-9f91-805c3490e0f6' -d '{"console": 2010, "name": "test"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/0991cab9-28ea-4dc3-942e-f81a296344d9' -d '{"console": 2010, "name": "test"}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/4dac8148-a8d2-42a5-9f91-805c3490e0f6 HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/0991cab9-28ea-4dc3-942e-f81a296344d9 HTTP/1.1 { "console": 2010, "name": "test" @@ -9,13 +9,14 @@ PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/virtualbox/vms/4dac8148-a8 HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 359 +CONTENT-LENGTH: 387 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} { + "acpi_shutdown": false, "adapter_type": "Intel PRO/1000 MT Desktop (82540EM)", "adapters": 0, "console": 2010, @@ -25,6 +26,6 @@ X-ROUTE: /v1/projects/{project_id}/virtualbox/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "ram": 0, "use_any_adapter": false, - "vm_id": "4dac8148-a8d2-42a5-9f91-805c3490e0f6", + "vm_id": "0991cab9-28ea-4dc3-942e-f81a296344d9", "vmname": "VMTEST" } diff --git a/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt b/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt index f0007143..5e788ecd 100644 --- a/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt +++ b/docs/api/examples/put_projectsprojectidvpcsvmsvmid.txt @@ -1,6 +1,6 @@ -curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/19169b30-8673-43e6-a096-11a6ebea1538' -d '{"console": 2011, "name": "test", "startup_script": "ip 192.168.1.1"}' +curl -i -X PUT 'http://localhost:8000/v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/fa77b9a7-efc7-4076-a733-a1cc56ef1042' -d '{"console": 2011, "name": "test", "startup_script": "ip 192.168.1.1"}' -PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/19169b30-8673-43e6-a096-11a6ebea1538 HTTP/1.1 +PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/fa77b9a7-efc7-4076-a733-a1cc56ef1042 HTTP/1.1 { "console": 2011, "name": "test", @@ -10,7 +10,7 @@ PUT /v1/projects/a1e920ca-338a-4e9f-b363-aa607b09dd80/vpcs/vms/19169b30-8673-43e HTTP/1.1 200 CONNECTION: keep-alive -CONTENT-LENGTH: 236 +CONTENT-LENGTH: 261 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 GNS3/1.4.0.dev1 @@ -22,5 +22,6 @@ X-ROUTE: /v1/projects/{project_id}/vpcs/vms/{vm_id} "project_id": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "startup_script": "ip 192.168.1.1", "startup_script_path": "startup.vpc", - "vm_id": "19169b30-8673-43e6-a096-11a6ebea1538" + "status": "stopped", + "vm_id": "fa77b9a7-efc7-4076-a733-a1cc56ef1042" } diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst index 1ff726ba..7f0e65e4 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceid.rst @@ -9,8 +9,8 @@ Get a Dynamips device instance Parameters ********** -- **device_id**: UUID for the instance - **project_id**: UUID for the project +- **device_id**: UUID for the instance Response status codes ********************** @@ -38,8 +38,8 @@ Update a Dynamips device instance Parameters ********** -- **device_id**: UUID for the instance - **project_id**: UUID for the project +- **device_id**: UUID for the instance Response status codes ********************** @@ -95,8 +95,8 @@ Delete a Dynamips device instance Parameters ********** -- **device_id**: UUID for the instance - **project_id**: UUID for the project +- **device_id**: UUID for the instance Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst index f74e9656..97b14b9d 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdnio.rst @@ -9,9 +9,9 @@ Add a NIO to a Dynamips device instance Parameters ********** +- **project_id**: UUID for the project - **device_id**: UUID for the instance - **port_number**: Port on the device -- **project_id**: UUID for the project Response status codes ********************** @@ -47,6 +47,17 @@ Linux Ethernet Network Input/Output type ✔ enum Possible values: nio_linux_ethernet +NAT +^^^^^^^^^^^^^^^^^^^^^^ +NAT Network Input/Output + +.. raw:: html + + + + +
Name Mandatory Type Description
type enum Possible values: nio_nat
+ NULL ^^^^^^^^^^^^^^^^^^^^^^ NULL Network Input/Output @@ -117,7 +128,7 @@ Body - +
Name Mandatory Type Description
mappings object
nio UDP, Ethernet, LinuxEthernet, TAP, UNIX, VDE, NULL
nio UDP, Ethernet, LinuxEthernet, NAT, TAP, UNIX, VDE, NULL
port_settings object Ethernet switch
@@ -128,9 +139,9 @@ Remove a NIO from a Dynamips device instance Parameters ********** +- **project_id**: UUID for the project - **device_id**: UUID for the instance - **port_number**: Port on the device -- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst index a33ba970..19852f49 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstartcapture.rst @@ -9,9 +9,9 @@ Start a packet capture on a Dynamips device instance Parameters ********** +- **project_id**: UUID for the project - **device_id**: UUID for the instance - **port_number**: Port on the device -- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst index 75cf96a4..cc312e43 100644 --- a/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst +++ b/docs/api/v1/dynamips_device/projectsprojectiddynamipsdevicesdeviceidportsportnumberdstopcapture.rst @@ -9,9 +9,9 @@ Stop a packet capture on a Dynamips device instance Parameters ********** +- **project_id**: UUID for the project - **device_id**: UUID for the instance - **port_number**: Port on the device -- **project_id**: UUID for the project Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/dynamipsvmsfilename.rst b/docs/api/v1/dynamips_vm/dynamipsvmsfilename.rst new file mode 100644 index 00000000..ed7e219c --- /dev/null +++ b/docs/api/v1/dynamips_vm/dynamipsvmsfilename.rst @@ -0,0 +1,13 @@ +/v1/dynamips/vms/{filename} +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/dynamips/vms/**{filename}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Upload Dynamips image. + +Response status codes +********************** +- **204**: Image uploaded + diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvms.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvms.rst index a8b15b28..72f3cf83 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvms.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvms.rst @@ -23,6 +23,7 @@ Input + @@ -73,6 +74,7 @@ Output
Name Mandatory Type Description
auto_delete_disks boolean automatically delete nvram and disk files
aux integer auxiliary console TCP port
chassis string router chassis model
clock_divisor integer clock divisor
+ diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst index 9f8b27db..85b6b979 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmid.rst @@ -24,6 +24,7 @@ Output
Name Mandatory Type Description
auto_delete_disks boolean automatically delete nvram and disk files
aux ['integer', 'null'] auxiliary console TCP port
chassis string router chassis model
clock_divisor integer clock divisor
+ @@ -90,6 +91,7 @@ Input
Name Mandatory Type Description
auto_delete_disks boolean automatically delete nvram and disk files
aux ['integer', 'null'] auxiliary console TCP port
chassis string router chassis model
clock_divisor integer clock divisor
+ @@ -136,6 +138,7 @@ Output
Name Mandatory Type Description
auto_delete_disks boolean automatically delete nvram and disk files
aux integer auxiliary console TCP port
chassis string router chassis model
clock_divisor integer clock divisor
+ diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 7886ad07..e08e9f1e 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a Dynamips VM instance Parameters ********** -- **port_number**: Port on the adapter - **vm_id**: UUID for the instance -- **adapter_number**: Adapter where the nio should be added - **project_id**: UUID for the project +- **adapter_number**: Adapter where the nio should be added +- **port_number**: Port on the adapter Response status codes ********************** @@ -27,10 +27,10 @@ Remove a NIO from a Dynamips VM instance Parameters ********** -- **port_number**: Port on the adapter - **vm_id**: UUID for the instance -- **adapter_number**: Adapter from where the nio should be removed - **project_id**: UUID for the project +- **adapter_number**: Adapter from where the nio should be removed +- **port_number**: Port on the adapter Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index 6cc4d452..8e041d67 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a Dynamips VM instance Parameters ********** -- **port_number**: Port on the adapter - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to start a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to start a packet capture +- **port_number**: Port on the adapter Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index ce104421..5df68984 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a Dynamips VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to stop a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to stop a packet capture +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidconfigs.rst b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidconfigs.rst index 547b9e25..d8aae9a1 100644 --- a/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidconfigs.rst +++ b/docs/api/v1/dynamips_vm/projectsprojectiddynamipsvmsvmidconfigs.rst @@ -19,7 +19,7 @@ Output
Name Mandatory Type Description
auto_delete_disks boolean automatically delete nvram and disk files
aux ['integer', 'null'] auxiliary console TCP port
chassis string router chassis model
clock_divisor integer clock divisor
- - + +
Name Mandatory Type Description
private_config_content ['string', 'null'] Content of the private configuration file
startup_config_content ['string', 'null'] Content of the startup configuration file
private_config_content ['string', 'null'] Content of the private configuration file
startup_config_content ['string', 'null'] Content of the startup configuration file
diff --git a/docs/api/v1/iou/iouvms.rst b/docs/api/v1/iou/iouvms.rst index 80be4a0c..466ae34c 100644 --- a/docs/api/v1/iou/iouvms.rst +++ b/docs/api/v1/iou/iouvms.rst @@ -11,3 +11,9 @@ Response status codes ********************** - **200**: List of IOU VM retrieved +Sample session +*************** + + +.. literalinclude:: ../../examples/get_iouvms.txt + diff --git a/docs/api/v1/iou/iouvmsfilename.rst b/docs/api/v1/iou/iouvmsfilename.rst new file mode 100644 index 00000000..3772f59a --- /dev/null +++ b/docs/api/v1/iou/iouvmsfilename.rst @@ -0,0 +1,13 @@ +/v1/iou/vms/{filename} +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/iou/vms/**{filename}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Upload IOU image. + +Response status codes +********************** +- **204**: Image uploaded + diff --git a/docs/api/v1/iou/projectsprojectidiouvms.rst b/docs/api/v1/iou/projectsprojectidiouvms.rst index e780349e..2ff983d8 100644 --- a/docs/api/v1/iou/projectsprojectidiouvms.rst +++ b/docs/api/v1/iou/projectsprojectidiouvms.rst @@ -25,15 +25,17 @@ Input Name Mandatory Type Description console ['integer', 'null'] console TCP port ethernet_adapters integer How many ethernet adapters are connected to the IOU - initial_config ['string', 'null'] Path to the initial configuration of IOU - initial_config_content ['string', 'null'] Initial configuration of IOU iourc_content ['string', 'null'] Content of the iourc file, if a file exist on servers this variable is ignored. It's mostly for compatibility with < 1.3 releases l1_keepalives ['boolean', 'null'] Always up ethernet interface name ✔ string IOU VM name nvram ['integer', 'null'] Allocated NVRAM KB path ✔ string Path of iou binary + private_config ['string', 'null'] Path to the private-config of IOU + private_config_content ['string', 'null'] Private-config of IOU ram ['integer', 'null'] Allocated RAM MB serial_adapters integer How many serial adapters are connected to the IOU + startup_config ['string', 'null'] Path to the startup-config of IOU + startup_config_content ['string', 'null'] Startup-config of IOU use_default_iou_values ['boolean', 'null'] Use default IOU values vm_id IOU VM identifier @@ -46,15 +48,16 @@ Output Name Mandatory Type Description console ✔ integer console TCP port ethernet_adapters ✔ integer How many ethernet adapters are connected to the IOU - initial_config ✔ ['string', 'null'] Path of the initial config content relative to project directory iourc_path ['string', 'null'] Path of the iourc file used by remote servers l1_keepalives ✔ boolean Always up ethernet interface name ✔ string IOU VM name nvram ✔ integer Allocated NVRAM KB path ✔ string Path of iou binary + private_config ✔ ['string', 'null'] Path of the private-config content relative to project directory project_id ✔ string Project UUID ram ✔ integer Allocated RAM MB serial_adapters ✔ integer How many serial adapters are connected to the IOU + startup_config ✔ ['string', 'null'] Path of the startup-config content relative to project directory use_default_iou_values ✔ ['boolean', 'null'] Use default IOU values vm_id ✔ string IOU VM UUID diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst index 6d98a4bc..4deb1d17 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmid.rst @@ -26,15 +26,16 @@ Output Name Mandatory Type Description console ✔ integer console TCP port ethernet_adapters ✔ integer How many ethernet adapters are connected to the IOU - initial_config ✔ ['string', 'null'] Path of the initial config content relative to project directory iourc_path ['string', 'null'] Path of the iourc file used by remote servers l1_keepalives ✔ boolean Always up ethernet interface name ✔ string IOU VM name nvram ✔ integer Allocated NVRAM KB path ✔ string Path of iou binary + private_config ✔ ['string', 'null'] Path of the private-config content relative to project directory project_id ✔ string Project UUID ram ✔ integer Allocated RAM MB serial_adapters ✔ integer How many serial adapters are connected to the IOU + startup_config ✔ ['string', 'null'] Path of the startup-config content relative to project directory use_default_iou_values ✔ ['boolean', 'null'] Use default IOU values vm_id ✔ string IOU VM UUID @@ -70,14 +71,15 @@ Input Name Mandatory Type Description console ['integer', 'null'] console TCP port ethernet_adapters ['integer', 'null'] How many ethernet adapters are connected to the IOU - initial_config_content ['string', 'null'] Initial configuration of IOU iourc_content ['string', 'null'] Content of the iourc file, if a file exist on servers this variable is ignored. It's mostly for compatibility with < 1.3 releases l1_keepalives ['boolean', 'null'] Always up ethernet interface name ['string', 'null'] IOU VM name nvram ['integer', 'null'] Allocated NVRAM KB path ['string', 'null'] Path of iou binary + private_config_content ['string', 'null'] Private-config of IOU ram ['integer', 'null'] Allocated RAM MB serial_adapters ['integer', 'null'] How many serial adapters are connected to the IOU + startup_config_content ['string', 'null'] Startup-config of IOU use_default_iou_values ['boolean', 'null'] Use default IOU values @@ -89,15 +91,16 @@ Output Name Mandatory Type Description console ✔ integer console TCP port ethernet_adapters ✔ integer How many ethernet adapters are connected to the IOU - initial_config ✔ ['string', 'null'] Path of the initial config content relative to project directory iourc_path ['string', 'null'] Path of the iourc file used by remote servers l1_keepalives ✔ boolean Always up ethernet interface name ✔ string IOU VM name nvram ✔ integer Allocated NVRAM KB path ✔ string Path of iou binary + private_config ✔ ['string', 'null'] Path of the private-config content relative to project directory project_id ✔ string Project UUID ram ✔ integer Allocated RAM MB serial_adapters ✔ integer How many serial adapters are connected to the IOU + startup_config ✔ ['string', 'null'] Path of the startup-config content relative to project directory use_default_iou_values ✔ ['boolean', 'null'] Use default IOU values vm_id ✔ string IOU VM UUID diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 4f203c58..ebec7d20 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a IOU instance Parameters ********** -- **port_number**: Port where the nio should be added - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port where the nio should be added Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a IOU instance Parameters ********** -- **port_number**: Port from where the nio should be removed - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port from where the nio should be removed Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index 8886e031..6e985b9d 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a IOU VM instance Parameters ********** -- **port_number**: Port on the adapter - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to start a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to start a packet capture +- **port_number**: Port on the adapter Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index a1b1d625..699eec12 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a IOU VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to stop a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to stop a packet capture +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidinitialconfig.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidconfigs.rst similarity index 52% rename from docs/api/v1/iou/projectsprojectidiouvmsvmidinitialconfig.rst rename to docs/api/v1/iou/projectsprojectidiouvmsvmidconfigs.rst index 6159e783..f9955b7e 100644 --- a/docs/api/v1/iou/projectsprojectidiouvmsvmidinitialconfig.rst +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidconfigs.rst @@ -1,15 +1,15 @@ -/v1/projects/{project_id}/iou/vms/{vm_id}/initial_config +/v1/projects/{project_id}/iou/vms/{vm_id}/configs ---------------------------------------------------------------------------------------------------------------------- .. contents:: -GET /v1/projects/**{project_id}**/iou/vms/**{vm_id}**/initial_config +GET /v1/projects/**{project_id}**/iou/vms/**{vm_id}**/configs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Retrieve the initial config content +Retrieve the startup and private configs content Response status codes ********************** -- **200**: Initial config retrieved +- **200**: Configs retrieved - **400**: Invalid request - **404**: Instance doesn't exist @@ -19,12 +19,13 @@ Output - + +
Name Mandatory Type Description
content ['string', 'null'] Content of the initial configuration file
private_config_content ['string', 'null'] Content of the private configuration file
startup_config_content ['string', 'null'] Content of the startup configuration file
Sample session *************** -.. literalinclude:: ../../examples/get_projectsprojectidiouvmsvmidinitialconfig.txt +.. literalinclude:: ../../examples/get_projectsprojectidiouvmsvmidconfigs.txt diff --git a/docs/api/v1/iou/projectsprojectidiouvmsvmidconfigssave.rst b/docs/api/v1/iou/projectsprojectidiouvmsvmidconfigssave.rst new file mode 100644 index 00000000..b212fc51 --- /dev/null +++ b/docs/api/v1/iou/projectsprojectidiouvmsvmidconfigssave.rst @@ -0,0 +1,15 @@ +/v1/projects/{project_id}/iou/vms/{vm_id}/configs/save +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/iou/vms/**{vm_id}**/configs/save +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Save the startup and private configs content + +Response status codes +********************** +- **200**: Configs saved +- **400**: Invalid request +- **404**: Instance doesn't exist + diff --git a/docs/api/v1/project/projectsprojectidfiles.rst b/docs/api/v1/project/projectsprojectidfiles.rst new file mode 100644 index 00000000..2c877216 --- /dev/null +++ b/docs/api/v1/project/projectsprojectidfiles.rst @@ -0,0 +1,24 @@ +/v1/projects/{project_id}/files +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/projects/**{project_id}**/files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +List files of a project + +Parameters +********** +- **project_id**: The UUID of the project + +Response status codes +********************** +- **200**: Return list of files +- **404**: The project doesn't exist + +Sample session +*************** + + +.. literalinclude:: ../../examples/get_projectsprojectidfiles.txt + diff --git a/docs/api/v1/project/projectsprojectidfilespath.rst b/docs/api/v1/project/projectsprojectidfilespath.rst new file mode 100644 index 00000000..4ce054ef --- /dev/null +++ b/docs/api/v1/project/projectsprojectidfilespath.rst @@ -0,0 +1,19 @@ +/v1/projects/{project_id}/files/{path:.+} +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/projects/**{project_id}**/files/**{path:.+}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Get a file of a project + +Parameters +********** +- **project_id**: The UUID of the project + +Response status codes +********************** +- **200**: Return the file +- **403**: Permission denied +- **404**: The file doesn't exist + diff --git a/docs/api/v1/project/projectsprojectidnotifications.rst b/docs/api/v1/project/projectsprojectidnotifications.rst new file mode 100644 index 00000000..7967deb0 --- /dev/null +++ b/docs/api/v1/project/projectsprojectidnotifications.rst @@ -0,0 +1,18 @@ +/v1/projects/{project_id}/notifications +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/projects/**{project_id}**/notifications +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Receive notifications about the projects + +Parameters +********** +- **project_id**: The UUID of the project + +Response status codes +********************** +- **200**: End of stream +- **404**: The project doesn't exist + diff --git a/docs/api/v1/qemu/projectsprojectidqemuvms.rst b/docs/api/v1/qemu/projectsprojectidqemuvms.rst index 62ce6757..0fbdb8f7 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvms.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvms.rst @@ -23,6 +23,7 @@ Input + @@ -34,7 +35,9 @@ Input + + @@ -49,6 +52,7 @@ Output
Name Mandatory Type Description
acpi_shutdown ['boolean', 'null'] ACPI shutdown support
adapter_type ['string', 'null'] QEMU adapter type
adapters ['integer', 'null'] number of adapters
console ['integer', 'null'] console TCP port
initrd ['string', 'null'] QEMU initrd path
kernel_command_line ['string', 'null'] QEMU kernel command line
kernel_image ['string', 'null'] QEMU kernel image path
kvm ['boolean', 'null'] KVM support
legacy_networking ['boolean', 'null'] Use QEMU legagy networking commands (-net syntax)
mac_address ['string', 'null'] QEMU MAC address
name string QEMU VM instance name
options ['string', 'null'] Additional QEMU options
process_priority enum Possible values: realtime, very high, high, normal, low, very low, null
+ @@ -60,7 +64,9 @@ Output + + diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst index 7cf89de6..758d6449 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmid.rst @@ -24,6 +24,7 @@ Output
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown support
adapter_type string QEMU adapter type
adapters integer number of adapters
console integer console TCP port
initrd string QEMU initrd path
kernel_command_line string QEMU kernel command line
kernel_image string QEMU kernel image path
kvm ['boolean', 'null'] KVM support
legacy_networking boolean Use QEMU legagy networking commands (-net syntax)
mac_address string QEMU MAC address
name string QEMU VM instance name
options string Additional QEMU options
process_priority enum Possible values: realtime, very high, high, normal, low, very low
+ @@ -35,7 +36,9 @@ Output + + @@ -74,6 +77,7 @@ Input
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown support
adapter_type string QEMU adapter type
adapters integer number of adapters
console integer console TCP port
initrd string QEMU initrd path
kernel_command_line string QEMU kernel command line
kernel_image string QEMU kernel image path
kvm ['boolean', 'null'] KVM support
legacy_networking boolean Use QEMU legagy networking commands (-net syntax)
mac_address string QEMU MAC address
name string QEMU VM instance name
options string Additional QEMU options
process_priority enum Possible values: realtime, very high, high, normal, low, very low
+ @@ -85,7 +89,9 @@ Input + + @@ -99,6 +105,7 @@ Output
Name Mandatory Type Description
acpi_shutdown ['boolean', 'null'] ACPI shutdown support
adapter_type ['string', 'null'] QEMU adapter type
adapters ['integer', 'null'] number of adapters
console ['integer', 'null'] console TCP port
initrd ['string', 'null'] QEMU initrd path
kernel_command_line ['string', 'null'] QEMU kernel command line
kernel_image ['string', 'null'] QEMU kernel image path
kvm ['boolean', 'null'] KVM support
legacy_networking ['boolean', 'null'] Use QEMU legagy networking commands (-net syntax)
mac_address ['string', 'null'] QEMU MAC address
name ['string', 'null'] QEMU VM instance name
options ['string', 'null'] Additional QEMU options
process_priority enum Possible values: realtime, very high, high, normal, low, very low, null
+ @@ -110,7 +117,9 @@ Output + + diff --git a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 71edd895..f06974ee 100644 --- a/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/qemu/projectsprojectidqemuvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a Qemu VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port on the adapter (always 0) Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a Qemu VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/qemu/qemuimgbinaries.rst b/docs/api/v1/qemu/qemuimgbinaries.rst new file mode 100644 index 00000000..596e0b69 --- /dev/null +++ b/docs/api/v1/qemu/qemuimgbinaries.rst @@ -0,0 +1,15 @@ +/v1/qemu/img-binaries +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/qemu/img-binaries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Get a list of available Qemu-img binaries + +Response status codes +********************** +- **200**: Success +- **400**: Invalid request +- **404**: Instance doesn't exist + diff --git a/docs/api/v1/qemu/qemuvmsfilename.rst b/docs/api/v1/qemu/qemuvmsfilename.rst new file mode 100644 index 00000000..b44f4ce2 --- /dev/null +++ b/docs/api/v1/qemu/qemuvmsfilename.rst @@ -0,0 +1,13 @@ +/v1/qemu/vms/{filename} +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/qemu/vms/**{filename}** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Upload Qemu image. + +Response status codes +********************** +- **204**: Image uploaded + diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvms.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvms.rst index a6a77682..e8463831 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvms.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvms.rst @@ -23,6 +23,7 @@ Input
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown support
adapter_type string QEMU adapter type
adapters integer number of adapters
console integer console TCP port
initrd string QEMU initrd path
kernel_command_line string QEMU kernel command line
kernel_image string QEMU kernel image path
kvm ['boolean', 'null'] KVM support
legacy_networking boolean Use QEMU legagy networking commands (-net syntax)
mac_address string QEMU MAC address
name string QEMU VM instance name
options string Additional QEMU options
process_priority enum Possible values: realtime, very high, high, normal, low, very low
+ @@ -42,6 +43,7 @@ Output
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown
adapter_type string VirtualBox adapter type
adapters integer number of adapters
console integer console TCP port
+ diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst index d345c454..7f8d3726 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmid.rst @@ -24,6 +24,7 @@ Output
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown
adapter_type string VirtualBox adapter type
adapters integer number of adapters
console integer console TCP port
+ @@ -66,6 +67,7 @@ Input
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown
adapter_type string VirtualBox adapter type
adapters integer number of adapters
console integer console TCP port
+ @@ -83,6 +85,7 @@ Output
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown
adapter_type string VirtualBox adapter type
adapters integer number of adapters
console integer console TCP port
+ diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst index c6e4393b..bbcc5e32 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a VirtualBox VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter where the nio should be added - **project_id**: UUID for the project +- **adapter_number**: Adapter where the nio should be added +- **port_number**: Port on the adapter (always 0) Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a VirtualBox VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter from where the nio should be removed - **project_id**: UUID for the project +- **adapter_number**: Adapter from where the nio should be removed +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst index ac55cd81..2d3c2f36 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstartcapture.rst @@ -9,10 +9,10 @@ Start a packet capture on a VirtualBox VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to start a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to start a packet capture +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst index 65899c26..664f5845 100644 --- a/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst +++ b/docs/api/v1/virtualbox/projectsprojectidvirtualboxvmsvmidadaptersadapternumberdportsportnumberdstopcapture.rst @@ -9,10 +9,10 @@ Stop a packet capture on a VirtualBox VM instance Parameters ********** -- **port_number**: Port on the adapter (always 0) - **vm_id**: UUID for the instance -- **adapter_number**: Adapter to stop a packet capture - **project_id**: UUID for the project +- **adapter_number**: Adapter to stop a packet capture +- **port_number**: Port on the adapter (always 0) Response status codes ********************** diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevms.rst b/docs/api/v1/vmware/projectsprojectidvmwarevms.rst index bb8ea8a1..f500d18c 100644 --- a/docs/api/v1/vmware/projectsprojectidvmwarevms.rst +++ b/docs/api/v1/vmware/projectsprojectidvmwarevms.rst @@ -23,11 +23,14 @@ Input
Name Mandatory Type Description
acpi_shutdown boolean ACPI shutdown
adapter_type string VirtualBox adapter type
adapters integer number of adapters
console integer console TCP port
+ + +
Name Mandatory Type Description
adapter_type string VMware adapter type
adapters integer number of adapters
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
linked_clone boolean either the VM is a linked clone or not
name string VMware VM instance name
use_any_adapter boolean allow GNS3 to use any VMware adapter
vm_id string VMware VM instance identifier
vmx_path string path to the vmx file
@@ -38,11 +41,14 @@ Output + + +
Name Mandatory Type Description
adapter_type string VMware adapter type
adapters integer number of adapters
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
use_any_adapter boolean allow GNS3 to use any VMware adapter
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst index 54445e72..246d7ce9 100644 --- a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmid.rst @@ -24,11 +24,14 @@ Output + + +
Name Mandatory Type Description
adapter_type string VMware adapter type
adapters integer number of adapters
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
use_any_adapter boolean allow GNS3 to use any VMware adapter
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
@@ -56,10 +59,13 @@ Input + + +
Name Mandatory Type Description
adapter_type string VMware adapter type
adapters integer number of adapters
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
use_any_adapter boolean allow GNS3 to use any VMware adapter
vmx_path string path to the vmx file
@@ -69,11 +75,14 @@ Output + + +
Name Mandatory Type Description
adapter_type string VMware adapter type
adapters integer number of adapters
console integer console TCP port
enable_remote_console boolean enable the remote console
headless boolean headless mode
name string VMware VM instance name
project_id string Project UUID
use_any_adapter boolean allow GNS3 to use any VMware adapter
vm_id string VMware VM instance UUID
vmx_path string path to the vmx file
diff --git a/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidadaptersadapternumberdportsportnumberdnio.rst new file mode 100644 index 00000000..67c32e7d --- /dev/null +++ b/docs/api/v1/vmware/projectsprojectidvmwarevmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -0,0 +1,40 @@ +/v1/projects/{project_id}/vmware/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +POST /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/adapters/**{adapter_number:\d+}**/ports/**{port_number:\d+}**/nio +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add a NIO to a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project +- **adapter_number**: Adapter where the nio should be added +- **port_number**: Port on the adapter (always 0) + +Response status codes +********************** +- **400**: Invalid request +- **201**: NIO created +- **404**: Instance doesn't exist + + +DELETE /v1/projects/**{project_id}**/vmware/vms/**{vm_id}**/adapters/**{adapter_number:\d+}**/ports/**{port_number:\d+}**/nio +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Remove a NIO from a VMware VM instance + +Parameters +********** +- **vm_id**: UUID for the instance +- **project_id**: UUID for the project +- **adapter_number**: Adapter from where the nio should be removed +- **port_number**: Port on the adapter (always 0) + +Response status codes +********************** +- **400**: Invalid request +- **404**: Instance doesn't exist +- **204**: NIO deleted + diff --git a/docs/api/v1/vmware/vmwarevms.rst b/docs/api/v1/vmware/vmwarevms.rst new file mode 100644 index 00000000..af366959 --- /dev/null +++ b/docs/api/v1/vmware/vmwarevms.rst @@ -0,0 +1,13 @@ +/v1/vmware/vms +---------------------------------------------------------------------------------------------------------------------- + +.. contents:: + +GET /v1/vmware/vms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Get all VMware VMs available + +Response status codes +********************** +- **200**: Success + diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvms.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvms.rst index 3bc1e779..90d6d894 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvms.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvms.rst @@ -40,6 +40,7 @@ Output project_id ✔ string Project UUID startup_script ['string', 'null'] Content of the VPCS startup script startup_script_path ✔ ['string', 'null'] Path of the VPCS startup script relative to project directory + status ✔ enum Possible values: started, stopped vm_id ✔ string VPCS VM UUID diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst index 0ff49d0a..455d54d8 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmid.rst @@ -29,6 +29,7 @@ Output project_id ✔ string Project UUID startup_script ['string', 'null'] Content of the VPCS startup script startup_script_path ✔ ['string', 'null'] Path of the VPCS startup script relative to project directory + status ✔ enum Possible values: started, stopped vm_id ✔ string VPCS VM UUID @@ -77,6 +78,7 @@ Output project_id ✔ string Project UUID startup_script ['string', 'null'] Content of the VPCS startup script startup_script_path ✔ ['string', 'null'] Path of the VPCS startup script relative to project directory + status ✔ enum Possible values: started, stopped vm_id ✔ string VPCS VM UUID diff --git a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst index 21a4ae02..92b8751d 100644 --- a/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst +++ b/docs/api/v1/vpcs/projectsprojectidvpcsvmsvmidadaptersadapternumberdportsportnumberdnio.rst @@ -9,10 +9,10 @@ Add a NIO to a VPCS instance Parameters ********** -- **port_number**: Port where the nio should be added - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port where the nio should be added Response status codes ********************** @@ -33,10 +33,10 @@ Remove a NIO from a VPCS instance Parameters ********** -- **port_number**: Port from where the nio should be removed - **vm_id**: UUID for the instance -- **adapter_number**: Network adapter where the nio is located - **project_id**: UUID for the project +- **adapter_number**: Network adapter where the nio is located +- **port_number**: Port from where the nio should be removed Response status codes ********************** diff --git a/tests/handlers/api/test_project.py b/tests/handlers/api/test_project.py index 852c27fd..843af8e2 100644 --- a/tests/handlers/api/test_project.py +++ b/tests/handlers/api/test_project.py @@ -233,7 +233,7 @@ def test_get_file(server, tmpdir): with open(os.path.join(project.path, "hello"), "w+") as f: f.write("world") - response = server.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True, example=True) + response = server.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True) assert response.status == 200 assert response.body == b"world" diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index e0cb9ff0..0fb269c5 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -202,7 +202,7 @@ def test_update_startup_script_h(vm): def test_get_startup_script(vm): content = "echo GNS3 VPCS\nip 192.168.1.2" vm.startup_script = content - assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS","ip 192.168.1.2"]) + assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) def test_get_startup_script_using_default_script(vm): From 2da0b36ee7fdeb2e99d8bcd0dad9d747f3297ca6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 10 Jun 2015 15:49:24 +0200 Subject: [PATCH 067/336] Support a platform parameter for Qemu I'm sure we will discover a distribution with a different naming conventions... Fix #220 --- gns3server/handlers/api/qemu_handler.py | 7 ++-- gns3server/modules/qemu/qemu_vm.py | 54 +++++++++++++++++++++---- gns3server/schemas/qemu.py | 20 +++++++-- tests/handlers/api/test_qemu.py | 21 ++++++++-- tests/modules/qemu/test_qemu_vm.py | 41 +++++++++++++++++-- 5 files changed, 123 insertions(+), 20 deletions(-) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index 426ca1a2..7b43d34e 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -51,9 +51,10 @@ class QEMUHandler: qemu = Qemu.instance() vm = yield from qemu.create_vm(request.json.pop("name"), request.match_info["project_id"], - request.json.get("vm_id"), - qemu_path=request.json.get("qemu_path"), - console=request.json.get("console")) + request.json.pop("vm_id", None), + qemu_path=request.json.pop("qemu_path", None), + platform=request.json.pop("platform", None), + console=request.json.pop("console", None)) for name, value in request.json.items(): if hasattr(vm, name) and getattr(vm, name) != value: diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 69aea17e..32c8f145 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -22,6 +22,7 @@ order to run a QEMU VM. import sys import os +import re import shutil import subprocess import shlex @@ -35,13 +36,16 @@ from ..nios.nio_udp import NIOUDP from ..nios.nio_tap import NIOTAP from ..nios.nio_nat import NIONAT from ..base_vm import BaseVM -from ...schemas.qemu import QEMU_OBJECT_SCHEMA +from ...schemas.qemu import QEMU_OBJECT_SCHEMA, QEMU_PLATFORMS from ...utils.asyncio import monitor_process import logging log = logging.getLogger(__name__) + + + class QemuVM(BaseVM): module_name = 'qemu' @@ -54,10 +58,11 @@ class QemuVM(BaseVM): :param manager: Manager instance :param console: TCP console port :param qemu_path: path to the QEMU binary + :param platform: Platform to emulate :param console: TCP console port """ - def __init__(self, name, vm_id, project, manager, qemu_path=None, console=None): + def __init__(self, name, vm_id, project, manager, qemu_path=None, console=None, platform=None): super().__init__(name, vm_id, project, manager, console=console) server_config = manager.config.get_section_config("Server") @@ -70,7 +75,18 @@ class QemuVM(BaseVM): self._stdout_file = "" # QEMU VM settings - self.qemu_path = qemu_path + + if qemu_path: + try: + self.qemu_path = qemu_path + except QemuError as e: + if platform: + self.platform = platform + else: + raise e + else: + self.platform = platform + self._hda_disk_image = "" self._hdb_disk_image = "" self._hdc_disk_image = "" @@ -124,6 +140,20 @@ class QemuVM(BaseVM): if qemu_path and os.pathsep not in qemu_path: qemu_path = shutil.which(qemu_path) + self._check_qemu_path(qemu_path) + self._qemu_path = qemu_path + self._platform = os.path.basename(qemu_path) + if self._platform == "qemu-kvm": + self._platform = "x86_64" + else: + self._platform = re.sub(r'^qemu-system-(.*)(w.exe)?$', r'\1', os.path.basename(qemu_path), re.IGNORECASE) + if self._platform not in QEMU_PLATFORMS: + raise QemuError("Platform {} is unknown".format(self._platform)) + log.info('QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(name=self._name, + id=self._id, + qemu_path=qemu_path)) + + def _check_qemu_path(self, qemu_path): if qemu_path is None: raise QemuError("QEMU binary path is not set or not found in the path") if not os.path.exists(qemu_path): @@ -131,10 +161,20 @@ class QemuVM(BaseVM): if not os.access(qemu_path, os.X_OK): raise QemuError("QEMU binary '{}' is not executable".format(qemu_path)) - self._qemu_path = qemu_path - log.info('QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(name=self._name, - id=self._id, - qemu_path=qemu_path)) + @property + def platform(self): + """ + Return the current platform + """ + return self._platform + + @platform.setter + def platform(self, platform): + self._platform = platform + if sys.platform.startswith("win"): + self.qemu_path = "qemu-system-{}w.exe".format(platform) + else: + self.qemu_path = "qemu-system-{}".format(platform) @property def hda_disk_image(self): diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 45a6dce7..40cd4f3b 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +QEMU_PLATFORMS = ["aarch64", "alpha", "arm", "cris", "i386", "lm32", "m68k", "microblaze", "microblazeel", "mips", "mips64", "mips64el", "mipsel", "moxie", "or32", "ppc", "ppc64", "ppcemb", "s390x", "sh4", "sh4eb", "sparc", "sparc64", "tricore", "unicore32", "x86_64", "xtensa", "xtensaeb"] + QEMU_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -38,9 +40,13 @@ QEMU_CREATE_SCHEMA = { }, "qemu_path": { "description": "Path to QEMU", - "type": "string", + "type": ["string", "null"], "minLength": 1, }, + "platform": { + "description": "Platform to emulate", + "enum": QEMU_PLATFORMS + ["null"] + }, "console": { "description": "console TCP port", "minimum": 1, @@ -130,7 +136,7 @@ QEMU_CREATE_SCHEMA = { }, }, "additionalProperties": False, - "required": ["name", "qemu_path"], + "required": ["name"], } QEMU_UPDATE_SCHEMA = { @@ -148,6 +154,10 @@ QEMU_UPDATE_SCHEMA = { "type": ["string", "null"], "minLength": 1, }, + "platform": { + "description": "Platform to emulate", + "enum": QEMU_PLATFORMS + ["null"] + }, "console": { "description": "console TCP port", "minimum": 1, @@ -264,6 +274,10 @@ QEMU_OBJECT_SCHEMA = { "type": "string", "minLength": 1, }, + "platform": { + "description": "Platform to emulate", + "enum": QEMU_PLATFORMS + }, "hda_disk_image": { "description": "QEMU hda disk image path", "type": "string", @@ -352,7 +366,7 @@ QEMU_OBJECT_SCHEMA = { }, }, "additionalProperties": False, - "required": ["vm_id", "project_id", "name", "qemu_path", "hda_disk_image", "hdb_disk_image", + "required": ["vm_id", "project_id", "name", "qemu_path", "platform", "hda_disk_image", "hdb_disk_image", "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "mac_address", "console", "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "acpi_shutdown", "kvm", "cpu_throttling", "process_priority", "options"] diff --git a/tests/handlers/api/test_qemu.py b/tests/handlers/api/test_qemu.py index cf08992a..c91fa792 100644 --- a/tests/handlers/api/test_qemu.py +++ b/tests/handlers/api/test_qemu.py @@ -25,7 +25,7 @@ from unittest.mock import patch @pytest.fixture def fake_qemu_bin(): - bin_path = os.path.join(os.environ["PATH"], "qemu_x42") + bin_path = os.path.join(os.environ["PATH"], "qemu_x86_64") with open(bin_path, "w+") as f: f.write("1") os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) @@ -51,7 +51,7 @@ def base_params(tmpdir, fake_qemu_bin): @pytest.fixture def fake_qemu_bin(): - bin_path = os.path.join(os.environ["PATH"], "qemu_x42") + bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64") with open(bin_path, "w+") as f: f.write("1") os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) @@ -65,12 +65,27 @@ def vm(server, project, base_params): return response.json -def test_qemu_create(server, project, base_params): +def test_qemu_create(server, project, base_params, fake_qemu_bin): response = server.post("/projects/{project_id}/qemu/vms".format(project_id=project.id), base_params) assert response.status == 201 assert response.route == "/projects/{project_id}/qemu/vms" assert response.json["name"] == "PC TEST 1" assert response.json["project_id"] == project.id + assert response.json["qemu_path"] == fake_qemu_bin + assert response.json["platform"] == "x86_64" + + +def test_qemu_create_platform(server, project, base_params, fake_qemu_bin): + base_params["qemu_path"] = None + base_params["platform"] = "x86_64" + + response = server.post("/projects/{project_id}/qemu/vms".format(project_id=project.id), base_params) + assert response.status == 201 + assert response.route == "/projects/{project_id}/qemu/vms" + assert response.json["name"] == "PC TEST 1" + assert response.json["project_id"] == project.id + assert response.json["qemu_path"] == fake_qemu_bin + assert response.json["platform"] == "x86_64" def test_qemu_create_with_params(server, project, base_params): diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 01b9b6f6..818782c8 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -52,9 +52,9 @@ def fake_qemu_img_binary(): def fake_qemu_binary(): if sys.platform.startswith("win"): - bin_path = os.path.join(os.environ["PATH"], "qemu_x42.EXE") + bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64.EXE") else: - bin_path = os.path.join(os.environ["PATH"], "qemu_x42") + bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64") with open(bin_path, "w+") as f: f.write("1") os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) @@ -171,7 +171,9 @@ def test_set_qemu_path(vm, tmpdir, fake_qemu_binary): vm.qemu_path = None # Should not crash with unicode characters - path = str(tmpdir / "bla\u62FF") + path = str(tmpdir / "\u62FF" / "qemu-system-mips") + + os.makedirs( str(tmpdir / "\u62FF") ) # Raise because file doesn't exists with pytest.raises(QemuError): @@ -189,14 +191,45 @@ def test_set_qemu_path(vm, tmpdir, fake_qemu_binary): vm.qemu_path = path assert vm.qemu_path == path + assert vm.platform == "mips" def test_set_qemu_path_environ(vm, tmpdir, fake_qemu_binary): # It should find the binary in the path - vm.qemu_path = "qemu_x42" + vm.qemu_path = "qemu-system-x86_64" assert vm.qemu_path == fake_qemu_binary + assert vm.platform == "x86_64" + + +@pytest.mark.skipif(sys.platform.startswith("linux") is False, reason="Supported only on linux") +def test_set_qemu_path_kvm_binary(vm, tmpdir, fake_qemu_binary): + + bin_path = os.path.join(os.environ["PATH"], "qemu-kvm") + with open(bin_path, "w+") as f: + f.write("1") + os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + return bin_path + + # It should find the binary in the path + vm.qemu_path = "qemu-kvm" + + assert vm.qemu_path == fake_qemu_binary + assert vm.platform == "x86_64" + + +def test_set_platform(project, manager): + + with patch("shutil.which", return_value="/bin/qemu-system-x86_64") as which_mock: + with patch("gns3server.modules.qemu.QemuVM._check_qemu_path"): + vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, platform="x86_64") + if sys.platform.startswith("win"): + which_mock.assert_called_with("qemu-system-x86_64w.exe") + else: + which_mock.assert_called_with("qemu-system-x86_64") + assert vm.platform == "x86_64" + assert vm.qemu_path == "/bin/qemu-system-x86_64" def test_disk_options(vm, loop, fake_qemu_img_binary): From 6e88ba4c2550bcb31966fbd340d3d93328842822 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 11 Jun 2015 17:07:13 +0200 Subject: [PATCH 068/336] Cleanup SSL certificate support Fix #208 --- README.rst | 28 ++++++++++- gns3server/cert_utils/create_cert.sh | 72 ++++------------------------ gns3server/server.py | 1 + 3 files changed, 37 insertions(+), 64 deletions(-) diff --git a/README.rst b/README.rst index 2ec6bcd1..d923bc86 100644 --- a/README.rst +++ b/README.rst @@ -107,7 +107,7 @@ You need to copy init/gns3.service.systemd to /lib/systemd/system/gns3.service .. code:: bash sudo chown root /lib/systemd/system/gns3.service - sudo + sudo systemctl start gns3 Windows ------- @@ -164,3 +164,29 @@ and homebrew: http://brew.sh/. gns3server +SSL +--- + +If you want enable SSL support on GNS3 you can generate a self signed certificate: + +.. code:: bash + + bassh gns3server/cert_utils/create_cert.sh + +This command will put the files in ~/.config/gns3/ssl on Linux and ~/.config/gns3.net/ssl on MacOSX. + +After you can start the server in SSL mode with: + +.. code:: bash + + python gns3server/main.py --certfile ~/.config/gns3.net/ssl/server.cert --certkey ~/.config/gns3.net/ssl/server.key --ssl + + +Or in your gns3_server.conf by adding in the Server section: + +.. code:: ini + + [Server] + certfile=/Users/noplay/.config/gns3.net/ssl/server.cert + certkey=/Users/noplay/.config/gns3.net/ssl/server.key + ssl=True diff --git a/gns3server/cert_utils/create_cert.sh b/gns3server/cert_utils/create_cert.sh index 92f6edfb..89f9a3fe 100755 --- a/gns3server/cert_utils/create_cert.sh +++ b/gns3server/cert_utils/create_cert.sh @@ -17,27 +17,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Bash shell script for generating self-signed certs. Run this in a folder, as it -# generates a few files. Large portions of this script were taken from the -# following artcile: -# -# http://usrportage.de/archives/919-Batch-generating-SSL-certificates.html -# -# Additional alterations by: Brad Landers -# Date: 2012-01-27 -# https://gist.github.com/bradland/1690807 - -# Script accepts a single argument, the fqdn for the cert - -DST_DIR="$HOME/.config/GNS3Certs/" -OLD_DIR=`pwd` +# Bash shell script for generating self-signed certs. +# The certicate is automaticaly put in your GNS3 config -#GNS3 Server expects to find certs with the default FQDN below. If you create -#different certs you will need to update server.py -DOMAIN="$1" -if [ -z "$DOMAIN" ]; then - DOMAIN="gns3server.localdomain.com" +if [[ "$OSTYPE" == "darwin"* ]]; then + DST_DIR="$HOME/.config/gns3.net/ssl" +else + DST_DIR="$HOME/.config/gns3/ssl" fi +OLD_DIR=`pwd` fail_if_error() { [ $1 != 0 ] && { @@ -52,48 +40,6 @@ mkdir -p $DST_DIR fail_if_error $? cd $DST_DIR +SUBJ="/C=CA/ST=Alberta/O=GNS3SELF/localityName=Calgary/commonName=localhost/organizationalUnitName=GNS3Server/emailAddress=gns3cert@gns3.com" -# Generate a passphrase -export PASSPHRASE=$(head -c 500 /dev/urandom | tr -dc a-z0-9A-Z | head -c 128; echo) - -# Certificate details; replace items in angle brackets with your own info -subj=" -C=CA -ST=Alberta -O=GNS3 -localityName=Calgary -commonName=$DOMAIN -organizationalUnitName=GNS3Server -emailAddress=gns3cert@gns3.com -" - -# Generate the server private key -openssl genrsa -aes256 -out $DOMAIN.key -passout env:PASSPHRASE 2048 -fail_if_error $? - -#openssl rsa -outform der -in $DOMAIN.pem -out $DOMAIN.key -passin env:PASSPHRASE - -# Generate the CSR -openssl req \ - -new \ - -batch \ - -subj "$(echo -n "$subj" | tr "\n" "/")" \ - -key $DOMAIN.key \ - -out $DOMAIN.csr \ - -passin env:PASSPHRASE -fail_if_error $? -cp $DOMAIN.key $DOMAIN.key.org -fail_if_error $? - -# Strip the password so we don't have to type it every time we restart Apache -openssl rsa -in $DOMAIN.key.org -out $DOMAIN.key -passin env:PASSPHRASE -fail_if_error $? - -# Generate the cert (good for 10 years) -openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt -fail_if_error $? - -echo "${DST_DIR}${DOMAIN}.key" -echo "${DST_DIR}${DOMAIN}.crt" - -cd $OLD_DIR \ No newline at end of file +openssl req -nodes -new -x509 -keyout server.key -out server.cert -subj "$SUBJ" diff --git a/gns3server/server.py b/gns3server/server.py index 77c194a9..ad75a550 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -163,6 +163,7 @@ class Server: except ssl.SSLError as e: log.critical("SSL error: {}".format(e)) raise SystemExit + log.info("SSL is enabled") return ssl_context @asyncio.coroutine From 04aac2f3dcdfe7d3d1fcd54ef8e304298038cf5d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 12 Jun 2015 09:40:38 +0200 Subject: [PATCH 069/336] Convert old -enable-kvm to kvm settings for Qemu Fix #233 --- gns3server/modules/qemu/qemu_vm.py | 10 +++++----- tests/modules/qemu/test_qemu_vm.py | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 32c8f145..38eb02a0 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -43,9 +43,6 @@ import logging log = logging.getLogger(__name__) - - - class QemuVM(BaseVM): module_name = 'qemu' @@ -346,7 +343,6 @@ class QemuVM(BaseVM): id=self._id, mac_addr=mac_address)) - @property def legacy_networking(self): """ @@ -510,7 +506,11 @@ class QemuVM(BaseVM): log.info('QEMU VM "{name}" [{id}] has set the QEMU options to {options}'.format(name=self._name, id=self._id, options=options)) - self._options = options + if "-enable-kvm" in options: + self.kvm = True + options = options.replace("-enable-kvm", "") + + self._options = options.strip() @property def initrd(self): diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 818782c8..82b5afa4 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -173,7 +173,7 @@ def test_set_qemu_path(vm, tmpdir, fake_qemu_binary): # Should not crash with unicode characters path = str(tmpdir / "\u62FF" / "qemu-system-mips") - os.makedirs( str(tmpdir / "\u62FF") ) + os.makedirs(str(tmpdir / "\u62FF")) # Raise because file doesn't exists with pytest.raises(QemuError): @@ -375,3 +375,17 @@ def test_hdd_disk_image(vm, tmpdir): assert vm.hdd_disk_image == "/tmp/test" vm.hdd_disk_image = "test" assert vm.hdd_disk_image == str(tmpdir / "QEMU" / "test") + + +def test_options(vm): + vm.kvm = False + vm.options = "-usb" + assert vm.options == "-usb" + assert vm.kvm is False + + +def test_options_kvm(vm): + vm.kvm = False + vm.options = "-usb -enable-kvm" + assert vm.options == "-usb" + assert vm.kvm is True From 4e16433a088702b347728b2591920bb8c8e7c90d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 12 Jun 2015 10:10:46 +0200 Subject: [PATCH 070/336] Limit file size during upload Fix #86 --- gns3server/templates/layout.html | 3 +++ gns3server/templates/upload.html | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/gns3server/templates/layout.html b/gns3server/templates/layout.html index 9ecbc82c..4f3da94b 100644 --- a/gns3server/templates/layout.html +++ b/gns3server/templates/layout.html @@ -1,6 +1,9 @@ + GNS3 Server diff --git a/gns3server/templates/upload.html b/gns3server/templates/upload.html index a894e467..3854e09f 100644 --- a/gns3server/templates/upload.html +++ b/gns3server/templates/upload.html @@ -1,8 +1,25 @@ {% extends "layout.html" %} +{% block script %} +function onSubmit() { + if (document.getElementById("uploadInput").files == undefined) { + //OLD browser + return true; + } + + max_size = 200; + var file = document.getElementById("uploadInput").files[0]; + var size = Math.round(file.size / 1000000); + if (size > max_size) { + alert("The file is too big (" + size + " MB). The max upload size is " + max_size + " MB. Please Upload your file with the GNS3 GUI"); + return false; + } + return true; +} +{% endblock %} {% block body %}

Select & Upload an image for GNS3

-
- File path:
+ + File path:
File type: