API for creating a VM

This commit is contained in:
Julien Duponchelle committed 2016-03-10 21:51:29 +01:00
1 parent 65de1b7b5e
commit 4326d412f9
14 files changed
+405 -48

No files matched your search

+28 -7
View File
@@ -19,6 +19,8 @@ import asyncio
import aiohttp
from ..config import Config
from .project import Project
from .hypervisor import Hypervisor
class Controller:
@@ -35,12 +37,17 @@ class Controller:
"""
return Config.instance().get_section_config("Server").getboolean("controller")
def addHypervisor(self, hypervisor):
@asyncio.coroutine
def addHypervisor(self, hypervisor_id, **kwargs):
"""
Add a server to the dictionnary of hypervisors controlled by GNS3
:param kwargs: See the documentation of Hypervisor
"""
if hypervisor.id not in self._hypervisors:
self._hypervisors[hypervisor.id] = hypervisor
if hypervisor_id not in self._hypervisors:
hypervisor = Hypervisor(hypervisor_id=hypervisor_id, **kwargs)
self._hypervisors[hypervisor_id] = hypervisor
return self._hypervisors[hypervisor_id]
@property
def hypervisors(self):
@@ -49,19 +56,33 @@ class Controller:
"""
return self._hypervisors
def getHypervisor(self, hypervisor_id):
"""
Return an hypervisor or raise a 404
"""
try:
return self._hypervisors[hypervisor_id]
except KeyError:
raise aiohttp.web.HTTPNotFound(text="Hypervisor ID {} doesn't exist".format(hypervisor_id))
@asyncio.coroutine
def addProject(self, project):
def addProject(self, project_id=None, **kwargs):
"""
Add a server to the dictionnary of projects controlled by GNS3
Create a project or return an existing project
:param kwargs: See the documentation of Project
"""
if project.id not in self._projects:
if project_id not in self._projects:
project = Project(project_id=project_id, **kwargs)
self._projects[project.id] = project
for hypervisor in self._hypervisors.values():
yield from project.addHypervisor(hypervisor)
return self._projects[project.id]
return self._projects[project_id]
def getProject(self, project_id):
"""
Return a server or raise a 404
Return a project or raise a 404
"""
try:
return self._projects[project_id]
+2 -2
View File
@@ -81,7 +81,7 @@ class Hypervisor:
}
@asyncio.coroutine
def _httpQuery(self, method, path, data=None):
def httpQuery(self, method, path, data=None):
with aiohttp.Timeout(10):
with aiohttp.ClientSession() as session:
url = "{}://{}:{}/v2/hypervisor{}".format(self._protocol, self._host, self._port, path)
@@ -98,4 +98,4 @@ class Hypervisor:
@asyncio.coroutine
def post(self, path, data={}):
yield from self._httpQuery("POST", path, data)
yield from self.httpQuery("POST", path, data)
+17
View File
@@ -18,6 +18,8 @@
import asyncio
from uuid import UUID, uuid4
from .vm import VM
class Project:
"""
@@ -42,6 +44,7 @@ class Project:
self._path = path
self._temporary = temporary
self._hypervisors = set()
self._vms = {}
@property
def name(self):
@@ -64,6 +67,20 @@ class Project:
self._hypervisors.add(hypervisor)
yield from hypervisor.post("/projects", self)
@asyncio.coroutine
def addVM(self, hypervisor, vm_id, **kwargs):
"""
Create a vm or return an existing vm
:param kwargs: See the documentation of VM
"""
if vm_id not in self._vms:
vm = VM(self, hypervisor, vm_id=vm_id, **kwargs)
yield from vm.create()
self._vms[vm.id] = vm
return vm
return self._vms[vm_id]
@asyncio.coroutine
def close(self):
for hypervisor in self._hypervisors:
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python
#
# Copyright (C) 2016 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# 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 <http://www.gnu.org/licenses/>.
import asyncio
import uuid
class VM:
def __init__(self, project, hypervisor, vm_id=None, vm_type=None, name=None, console=None, console_type="telnet", **kwargs):
"""
:param project: Project of the VM
:param hypervisor: Hypervisor server where the server will run
:param vm_id: UUID of the vm. Integer id
:param vm_type: Type of emulator
:param name: Name of the vm
:param console: TCP port of the console
:param console_type: Type of the console (telnet, vnc, serial..)
:param kwargs: Emulator specific properties of the VM
"""
if vm_id is None:
self._id = str(uuid.uuid4())
else:
self._id = vm_id
self._name = name
self._project = project
self._hypervisor = hypervisor
self._vm_type = vm_type
self._console = console
self._console_type = console_type
self._properties = kwargs
@property
def id(self):
return self._id
@property
def vm_type(self):
return self._vm_type
@property
def console(self):
return self._console
@property
def console_type(self):
return self._console_type
@property
def properties(self):
return self._properties
@property
def project(self):
return self._project
@asyncio.coroutine
def create(self):
data = self._properties
data["vm_id"] = self._id
data["console"] = self._console
data["console_type"] = self._console_type
yield from self._hypervisor.post("/projects/{}/{}/vms".format(self._project.id, self._vm_type), data=data)
def __json__(self):
return {
"hypervisor_id": self._hypervisor.id,
"vm_id": self._id,
"vm_type": self._vm_type,
"name": self._name,
"console": self._console,
"console_type": self._console_type,
"properties": self._properties
}
@@ -18,3 +18,5 @@
from .hypervisor_handler import HypervisorHandler
from .project_handler import ProjectHandler
from .version_handler import VersionHandler
from .vm_handler import VMHandler
@@ -44,9 +44,7 @@ class HypervisorHandler:
output=HYPERVISOR_OBJECT_SCHEMA)
def create(request, response):
hypervisor = Hypervisor(request.json.pop("hypervisor_id"), **request.json)
Controller.instance().addHypervisor(hypervisor)
hypervisor = yield from Controller.instance().addHypervisor(**request.json)
response.set_status(201)
response.json(hypervisor)
@@ -22,7 +22,6 @@ import asyncio
from ....web.route import Route
from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA
from ....controller import Controller
from ....controller.project import Project
import logging
@@ -44,11 +43,11 @@ class ProjectHandler:
def create_project(request, response):
controller = Controller.instance()
project = Project(name=request.json.get("name"),
path=request.json.get("path"),
project_id=request.json.get("project_id"),
temporary=request.json.get("temporary", False))
yield from controller.addProject(project)
project = yield from controller.addProject(
name=request.json.get("name"),
path=request.json.get("path"),
project_id=request.json.get("project_id"),
temporary=request.json.get("temporary", False))
response.set_status(201)
response.json(project)
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# 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 <http://www.gnu.org/licenses/>.
from ....web.route import Route
from ....schemas.vm import VM_OBJECT_SCHEMA
from ....controller.project import Project
from ....controller import Controller
class VMHandler:
"""
API entry point for VM
"""
@classmethod
@Route.post(
r"/projects/{project_id}/vms",
parameters={
"project_id": "UUID for the project"
},
status_codes={
201: "Instance created",
400: "Invalid request"
},
description="Create a new VM instance",
input=VM_OBJECT_SCHEMA,
output=VM_OBJECT_SCHEMA)
def create(request, response):
controller = Controller.instance()
hypervisor = controller.getHypervisor(request.json.pop("hypervisor_id"))
project = controller.getProject(request.match_info["project_id"])
vm = yield from project.addVM(hypervisor, request.json.pop("vm_id", None), **request.json)
response.set_status(201)
response.json(vm)
+45
View File
@@ -62,3 +62,48 @@ VM_CAPTURE_SCHEMA = {
"additionalProperties": False,
"required": ["capture_file_name"]
}
VM_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "A VM object",
"type": "object",
"properties": {
"hypervisor_id": {
"description": "Server identifier",
"type": "string"
},
"vm_id": {
"description": "VM 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}$"
},
"vm_type": {
"description": "Type of VM",
"enum": ["docker", "dynamips", "vpcs", "virtualbox", "vmware", "iou"]
},
"name": {
"description": "VM name",
"type": "string",
"minLength": 1,
},
"console": {
"description": "Console TCP port",
"minimum": 1,
"maximum": 65535,
"type": ["integer", "null"]
},
"console_type": {
"description": "Console type",
"enum": ["serial", "vnc", "telnet"]
},
"properties": {
"description": "Properties specific to an emulator",
"type": "object"
}
},
"additionalProperties": False,
"required": ["name", "vm_type", "hypervisor_id"]
}