diff --git a/docs/api/examples/post_virtualbox.txt b/docs/api/examples/post_virtualbox.txt
index 0f7721fa..6be43ee6 100644
--- a/docs/api/examples/post_virtualbox.txt
+++ b/docs/api/examples/post_virtualbox.txt
@@ -19,5 +19,5 @@ X-ROUTE: /virtualbox
{
"name": "VM1",
"project_uuid": "a1e920ca-338a-4e9f-b363-aa607b09dd80",
- "uuid": "a028124a-9a69-4b06-b673-21f7eb3d034f"
+ "uuid": "3142e932-d316-40d7-bed3-7ef8e2d313b3"
}
diff --git a/gns3server/handlers/virtualbox_handler.py b/gns3server/handlers/virtualbox_handler.py
index d6b944cc..55d9a53b 100644
--- a/gns3server/handlers/virtualbox_handler.py
+++ b/gns3server/handlers/virtualbox_handler.py
@@ -32,6 +32,7 @@ class VirtualBoxHandler:
r"/virtualbox",
status_codes={
201: "VirtualBox VM instance created",
+ 400: "Invalid project UUID",
409: "Conflict"
},
description="Create a new VirtualBox VM instance",
@@ -40,8 +41,10 @@ class VirtualBoxHandler:
def create(request, response):
vbox_manager = VirtualBox.instance()
- vm = yield from vbox_manager.create_vm(request.json["name"], request.json["project_uuid"], request.json.get("uuid"))
- print(vm)
+ vm = yield from vbox_manager.create_vm(request.json["name"],
+ request.json["project_uuid"],
+ request.json.get("uuid"),
+ vmname=request.json["vmname"])
response.json({"name": vm.name,
"uuid": vm.uuid,
"project_uuid": vm.project.uuid})
diff --git a/gns3server/handlers/vpcs_handler.py b/gns3server/handlers/vpcs_handler.py
index 54d32af2..5d4e9ac8 100644
--- a/gns3server/handlers/vpcs_handler.py
+++ b/gns3server/handlers/vpcs_handler.py
@@ -33,6 +33,7 @@ class VPCSHandler:
r"/vpcs",
status_codes={
201: "VPCS instance created",
+ 400: "Invalid project UUID",
409: "Conflict"
},
description="Create a new VPCS instance",
@@ -43,7 +44,7 @@ class VPCSHandler:
vpcs = VPCS.instance()
vm = yield from vpcs.create_vm(request.json["name"],
request.json["project_uuid"],
- uuid=request.json.get("uuid"),
+ request.json.get("uuid"),
console=request.json.get("console"),
script_file=request.json.get("script_file"))
response.json(vm)
diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py
index 7d51cb9f..3dcbca25 100644
--- a/gns3server/modules/base_manager.py
+++ b/gns3server/modules/base_manager.py
@@ -20,6 +20,7 @@ import asyncio
import aiohttp
from uuid import UUID, uuid4
+from ..config import Config
from .project_manager import ProjectManager
@@ -34,6 +35,7 @@ class BaseManager:
self._vms = {}
self._port_manager = None
+ self._config = Config.instance()
@classmethod
def instance(cls):
@@ -50,7 +52,7 @@ class BaseManager:
@property
def port_manager(self):
"""
- Returns the port_manager for this VMs
+ Returns the port manager.
:returns: Port manager
"""
@@ -62,6 +64,16 @@ class BaseManager:
self._port_manager = new_port_manager
+ @property
+ def config(self):
+ """
+ Returns the server config.
+
+ :returns: Config
+ """
+
+ return self._config
+
@classmethod
@asyncio.coroutine # FIXME: why coroutine?
def destroy(cls):
@@ -87,25 +99,23 @@ class BaseManager:
return self._vms[uuid]
@asyncio.coroutine
- def create_vm(self, name, project_identifier, uuid=None, **kwargs):
+ def create_vm(self, name, project_uuid, uuid, *args, **kwargs):
"""
Create a new VM
- :param name VM name
- :param project_identifier UUID of Project
- :param uuid Force UUID force VM
+ :param name: VM name
+ :param project_uuid: UUID of Project
+ :param uuid: restore a VM UUID
"""
- project = ProjectManager.instance().get_project(project_identifier)
+ project = ProjectManager.instance().get_project(project_uuid)
# TODO: support for old projects VM with normal IDs.
- # TODO: supports specific args: pass kwargs to VM_CLASS?
-
if not uuid:
uuid = str(uuid4())
- vm = self._VM_CLASS(name, uuid, project, self, **kwargs)
+ vm = self._VM_CLASS(name, uuid, project, self, *args, **kwargs)
future = vm.create()
if isinstance(future, asyncio.Future):
yield from future
diff --git a/gns3server/modules/base_vm.py b/gns3server/modules/base_vm.py
index cecd8432..001d5b1e 100644
--- a/gns3server/modules/base_vm.py
+++ b/gns3server/modules/base_vm.py
@@ -15,7 +15,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from ..config import Config
import logging
log = logging.getLogger(__name__)
@@ -29,13 +28,17 @@ class BaseVM:
self._uuid = uuid
self._project = project
self._manager = manager
- self._config = Config.instance()
# TODO: When delete release console ports
@property
def project(self):
- """Return VM current project"""
+ """
+ Returns the VM current project.
+
+ :returns: Project instance.
+ """
+
return self._project
@property
diff --git a/gns3server/modules/port_manager.py b/gns3server/modules/port_manager.py
index ad90d92a..31a38a4e 100644
--- a/gns3server/modules/port_manager.py
+++ b/gns3server/modules/port_manager.py
@@ -147,7 +147,7 @@ class PortManager:
else:
continue
- raise HTTPConflict(reason="Could not find a free port between {} and {} on host {}, last exception: {}".format(start_port,
+ raise HTTPConflict(text="Could not find a free port between {} and {} on host {}, last exception: {}".format(start_port,
end_port,
host,
last_exception))
@@ -174,7 +174,7 @@ class PortManager:
"""
if port in self._used_tcp_ports:
- raise HTTPConflict(reason="TCP port already {} in use on host".format(port, self._console_host))
+ raise HTTPConflict(text="TCP port already {} in use on host".format(port, self._console_host))
self._used_tcp_ports.add(port)
return port
@@ -209,7 +209,7 @@ class PortManager:
"""
if port in self._used_udp_ports:
- raise Exception("UDP port already {} in use on host".format(port, self._host))
+ raise HTTPConflict(text="UDP port already {} in use on host".format(port, self._console_host))
self._used_udp_ports.add(port)
def release_udp_port(self, port):
diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py
index 26694e21..9d98519c 100644
--- a/gns3server/modules/project.py
+++ b/gns3server/modules/project.py
@@ -15,13 +15,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import aiohttp
import os
import tempfile
-from uuid import uuid4
+from uuid import UUID, uuid4
class Project:
-
"""
A project contains a list of VM.
In theory VM are isolated project/project.
@@ -35,17 +35,23 @@ class Project:
if uuid is None:
self._uuid = str(uuid4())
else:
- assert len(uuid) == 36
+ try:
+ UUID(uuid, version=4)
+ except ValueError:
+ raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(uuid))
self._uuid = uuid
self._location = location
if location is None:
self._location = tempfile.mkdtemp()
- self._path = os.path.join(self._location, self._uuid)
- if os.path.exists(self._path) is False:
- os.mkdir(self._path)
- os.mkdir(os.path.join(self._path, "vms"))
+ self._path = os.path.join(self._location, self._uuid, "vms")
+ try:
+ os.makedirs(self._path)
+ except FileExistsError:
+ pass
+ except OSError as e:
+ raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
@property
def uuid(self):
@@ -62,17 +68,21 @@ class Project:
return self._path
- def vm_working_directory(self, vm_identifier):
+ def vm_working_directory(self, vm_uuid):
"""
Return a working directory for a specific VM.
If the directory doesn't exist, the directory is created.
- :param vm_identifier: UUID of VM
+ :param vm_uuid: VM UUID
"""
- path = os.path.join(self._path, 'vms', vm_identifier)
- if os.path.exists(path) is False:
- os.mkdir(path)
+ path = os.path.join(self._path, "vms", vm_uuid)
+ try:
+ os.makedirs(self._path)
+ except FileExistsError:
+ pass
+ except OSError as e:
+ raise aiohttp.web.HTTPInternalServerError(text="Could not create VM working directory: {}".format(e))
return path
def __json__(self):
diff --git a/gns3server/modules/project_manager.py b/gns3server/modules/project_manager.py
index cad199c4..f44ab0fd 100644
--- a/gns3server/modules/project_manager.py
+++ b/gns3server/modules/project_manager.py
@@ -17,6 +17,7 @@
import aiohttp
from .project import Project
+from uuid import UUID
class ProjectManager:
@@ -40,20 +41,23 @@ class ProjectManager:
cls._instance = cls()
return cls._instance
- def get_project(self, project_id):
+ def get_project(self, project_uuid):
"""
Returns a Project instance.
- :param project_id: Project identifier
+ :param project_uuid: Project UUID
:returns: Project instance
"""
- assert len(project_id) == 36
+ try:
+ UUID(project_uuid, version=4)
+ except ValueError:
+ raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(project_uuid))
- if project_id not in self._projects:
- raise aiohttp.web.HTTPNotFound(text="Project UUID {} doesn't exist".format(project_id))
- return self._projects[project_id]
+ if project_uuid not in self._projects:
+ raise aiohttp.web.HTTPNotFound(text="Project UUID {} doesn't exist".format(project_uuid))
+ return self._projects[project_uuid]
def create_project(self, **kwargs):
"""
diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py
index f922e681..9a61f76b 100644
--- a/gns3server/modules/virtualbox/virtualbox_vm.py
+++ b/gns3server/modules/virtualbox/virtualbox_vm.py
@@ -29,6 +29,7 @@ import json
import socket
import time
import asyncio
+import shutil
from .virtualbox_error import VirtualBoxError
from ..adapters.ethernet_adapter import EthernetAdapter
@@ -45,41 +46,44 @@ log = logging.getLogger(__name__)
class VirtualBoxVM(BaseVM):
-
"""
VirtualBox VM implementation.
"""
- _instances = []
- _allocated_console_ports = []
-
- def __init__(self, name, uuid, project, manager):
+ def __init__(self, name, uuid, project, manager, vmname, linked_clone):
super().__init__(name, uuid, project, manager)
+ # look for VBoxManage
+ self._vboxmanage_path = manager.config.get_section_config("VirtualBox").get("vboxmanage_path")
+ if not self._vboxmanage_path:
+ if sys.platform.startswith("win"):
+ if "VBOX_INSTALL_PATH" in os.environ:
+ self._vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
+ elif "VBOX_MSI_INSTALL_PATH" in os.environ:
+ self._vboxmanage_path = os.path.join(os.environ["VBOX_MSI_INSTALL_PATH"], "VBoxManage.exe")
+ elif sys.platform.startswith("darwin"):
+ self._vboxmanage_path = "/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
+ else:
+ self._vboxmanage_path = shutil.which("vboxmanage")
+
+ if not self._vboxmanage_path:
+ raise VirtualBoxError("Could not find VBoxManage")
+ if not os.access(self._vboxmanage_path, os.X_OK):
+ raise VirtualBoxError("VBoxManage is not executable")
+
+ self._vmname = vmname
+ self._started = False
+ self._linked_clone = linked_clone
self._system_properties = {}
-
- # FIXME: harcoded values
- if sys.platform.startswith("win"):
- self._vboxmanage_path = r"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe"
- else:
- self._vboxmanage_path = "/usr/bin/vboxmanage"
-
self._queue = asyncio.Queue()
self._created = asyncio.Future()
self._worker = asyncio.async(self._run())
return
- self._linked_clone = linked_clone
- self._working_dir = None
self._command = []
- self._vboxmanage_path = vboxmanage_path
self._vbox_user = vbox_user
- self._started = False
- self._console_host = console_host
- self._console_start_port_range = console_start_port_range
- self._console_end_port_range = console_end_port_range
self._telnet_server_thread = None
self._serial_pipe = None
@@ -101,28 +105,6 @@ class VirtualBoxVM(BaseVM):
# create the device own working directory
self.working_dir = working_dir_path
- if not self._console:
- # allocate a console port
- try:
- self._console = find_unused_port(self._console_start_port_range,
- self._console_end_port_range,
- self._console_host,
- ignore_ports=self._allocated_console_ports)
- except Exception as e:
- raise VirtualBoxError(e)
-
- if self._console in self._allocated_console_ports:
- raise VirtualBoxError("Console port {} is already used by another VirtualBox VM".format(console))
- self._allocated_console_ports.append(self._console)
-
- self._system_properties = {}
- properties = self._execute("list", ["systemproperties"])
- for prop in properties:
- try:
- name, value = prop.split(':', 1)
- except ValueError:
- continue
- self._system_properties[name.strip()] = value.strip()
if linked_clone:
if vbox_id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py
index dfeda3e4..00ad5a73 100644
--- a/gns3server/modules/vpcs/vpcs_vm.py
+++ b/gns3server/modules/vpcs/vpcs_vm.py
@@ -16,7 +16,7 @@
# along with this program. If not, see .
"""
-VPCS vm management (creates command line, processes, files etc.) in
+VPCS VM management (creates command line, processes, files etc.) in
order to run an VPCS instance.
"""
@@ -59,7 +59,7 @@ class VPCSVM(BaseVM):
super().__init__(name, uuid, project, manager)
- self._path = self._config.get_section_config("VPCS").get("path", "vpcs")
+ self._path = manager.config.get_section_config("VPCS").get("path", "vpcs")
self._console = console
diff --git a/gns3server/web/route.py b/gns3server/web/route.py
index cd1ece5d..b59acb4a 100644
--- a/gns3server/web/route.py
+++ b/gns3server/web/route.py
@@ -111,8 +111,8 @@ class Route(object):
response.json({"message": e.text, "status": e.status})
except VMError as e:
response = Response(route=route)
- response.set_status(400)
- response.json({"message": str(e), "status": 400})
+ response.set_status(500)
+ response.json({"message": str(e), "status": 500})
return response
cls._routes.append((method, cls._path, control_schema))