2015-02-19 20:43:45 +02:00
# -*- 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 <http://www.gnu.org/licenses/>.
2015-07-22 07:58:28 +03:00
import sys
2015-07-28 17:05:48 +03:00
import os . path
2015-04-27 23:19:17 +03:00
from aiohttp . web import HTTPConflict
2015-02-23 12:27:07 +02:00
from . . . web . route import Route
2015-07-22 07:58:28 +03:00
from . . . modules . project_manager import ProjectManager
2015-04-27 23:19:17 +03:00
from . . . schemas . nio import NIO_SCHEMA
2015-02-23 12:27:07 +02:00
from . . . schemas . qemu import QEMU_CREATE_SCHEMA
from . . . schemas . qemu import QEMU_UPDATE_SCHEMA
from . . . schemas . qemu import QEMU_OBJECT_SCHEMA
2016-01-01 02:40:12 +02:00
from . . . schemas . qemu import QEMU_BINARY_FILTER_SCHEMA
2015-02-23 12:27:07 +02:00
from . . . schemas . qemu import QEMU_BINARY_LIST_SCHEMA
2016-01-01 02:40:12 +02:00
from . . . schemas . qemu import QEMU_CAPABILITY_LIST_SCHEMA
2015-07-28 18:50:57 +03:00
from . . . schemas . qemu import QEMU_IMAGE_CREATE_SCHEMA
2015-10-05 12:07:15 +03:00
from . . . schemas . vm import VM_LIST_IMAGES_SCHEMA
2015-02-23 12:27:07 +02:00
from . . . modules . qemu import Qemu
2015-07-28 17:05:48 +03:00
from . . . config import Config
2015-02-19 20:43:45 +02:00
class QEMUHandler :
"""
API entry points for QEMU .
"""
@classmethod
@Route.post (
r " /projects/ {project_id} /qemu/vms " ,
parameters = {
" project_id " : " UUID for the project "
} ,
status_codes = {
201 : " Instance created " ,
400 : " Invalid request " ,
409 : " Conflict "
} ,
2015-04-13 00:09:37 +03:00
description = " Create a new Qemu VM instance " ,
2015-02-19 20:43:45 +02:00
input = QEMU_CREATE_SCHEMA ,
output = QEMU_OBJECT_SCHEMA )
def create ( request , response ) :
qemu = Qemu . instance ( )
2015-04-13 00:09:37 +03:00
vm = yield from qemu . create_vm ( request . json . pop ( " name " ) ,
2015-02-19 20:43:45 +02:00
request . match_info [ " project_id " ] ,
2015-06-10 16:49:24 +03:00
request . json . pop ( " vm_id " , None ) ,
2015-10-19 04:19:27 +03:00
linked_clone = request . json . get ( " linked_clone " , True ) ,
2015-06-10 16:49:24 +03:00
qemu_path = request . json . pop ( " qemu_path " , None ) ,
2015-07-04 01:06:25 +03:00
console = request . json . pop ( " console " , None ) ,
console_type = request . json . pop ( " console_type " , " telnet " ) ,
platform = request . json . pop ( " platform " , None ) )
2015-03-16 20:45:21 +02:00
2015-04-13 00:09:37 +03:00
for name , value in request . json . items ( ) :
if hasattr ( vm , name ) and getattr ( vm , name ) != value :
setattr ( vm , name , value )
2015-02-19 21:22:30 +02:00
2015-02-19 20:43:45 +02:00
response . set_status ( 201 )
response . json ( vm )
@classmethod
@Route.get (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Get a Qemu VM instance " ,
2015-02-19 20:43:45 +02:00
output = QEMU_OBJECT_SCHEMA )
def show ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_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} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Update a Qemu VM instance " ,
2015-02-19 20:43:45 +02:00
input = QEMU_UPDATE_SCHEMA ,
output = QEMU_OBJECT_SCHEMA )
def update ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
2015-04-13 00:09:37 +03:00
for name , value in request . json . items ( ) :
if hasattr ( vm , name ) and getattr ( vm , name ) != value :
setattr ( vm , name , value )
2015-02-19 20:43:45 +02:00
response . json ( vm )
@classmethod
@Route.delete (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Delete a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def delete ( request , response ) :
yield from Qemu . instance ( ) . delete_vm ( request . match_info [ " vm_id " ] )
response . set_status ( 204 )
@classmethod
@Route.post (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Start a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def start ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
2015-07-22 07:58:28 +03:00
if sys . platform . startswith ( " linux " ) and qemu_manager . config . get_section_config ( " Qemu " ) . getboolean ( " enable_kvm " , True ) \
and " -no-kvm " not in vm . options :
pm = ProjectManager . instance ( )
if pm . check_hardware_virtualization ( vm ) is False :
2015-07-27 04:21:30 +03:00
raise HTTPConflict ( text = " Cannot start VM with KVM enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox " )
2015-02-19 20:43:45 +02:00
yield from vm . start ( )
response . set_status ( 204 )
@classmethod
@Route.post (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Stop a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def stop ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_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} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Reload a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def reload ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
yield from vm . reload ( )
response . set_status ( 204 )
@classmethod
@Route.post (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Suspend a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def suspend ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
yield from vm . suspend ( )
response . set_status ( 204 )
2015-02-21 01:15:56 +02:00
@classmethod
@Route.post (
r " /projects/ {project_id} /qemu/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 "
} ,
2015-04-13 00:09:37 +03:00
description = " Resume a Qemu VM instance " )
2015-02-21 01:15:56 +02:00
def resume ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
yield from vm . resume ( )
response . set_status ( 204 )
2015-02-19 20:43:45 +02:00
@Route.post (
r " /projects/ {project_id} /qemu/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 " : " Network adapter where the nio is located " ,
2015-03-07 05:08:00 +02:00
" port_number " : " Port on the adapter (always 0) "
2015-02-19 20:43:45 +02:00
} ,
status_codes = {
201 : " NIO created " ,
400 : " Invalid request " ,
404 : " Instance doesn ' t exist "
} ,
2015-04-13 00:09:37 +03:00
description = " Add a NIO to a Qemu VM instance " ,
2015-04-27 23:19:17 +03:00
input = NIO_SCHEMA ,
output = NIO_SCHEMA )
2015-02-19 20:43:45 +02:00
def create_nio ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
2015-04-27 23:19:17 +03:00
nio_type = request . json [ " type " ]
2015-05-06 23:59:01 +03:00
if nio_type not in ( " nio_udp " , " nio_tap " , " nio_nat " ) :
2015-04-27 23:19:17 +03:00
raise HTTPConflict ( text = " NIO of type {} is not supported " . format ( nio_type ) )
2015-02-19 20:43:45 +02:00
nio = qemu_manager . create_nio ( vm . qemu_path , request . json )
2015-03-07 05:08:00 +02:00
yield from vm . adapter_add_nio_binding ( int ( request . match_info [ " adapter_number " ] ) , nio )
2015-02-19 20:43:45 +02:00
response . set_status ( 201 )
response . json ( nio )
@classmethod
@Route.delete (
r " /projects/ {project_id} /qemu/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 " : " Network adapter where the nio is located " ,
2015-03-07 05:08:00 +02:00
" port_number " : " Port on the adapter (always 0) "
2015-02-19 20:43:45 +02:00
} ,
status_codes = {
204 : " NIO deleted " ,
400 : " Invalid request " ,
404 : " Instance doesn ' t exist "
} ,
2015-04-13 00:09:37 +03:00
description = " Remove a NIO from a Qemu VM instance " )
2015-02-19 20:43:45 +02:00
def delete_nio ( request , response ) :
qemu_manager = Qemu . instance ( )
vm = qemu_manager . get_vm ( request . match_info [ " vm_id " ] , project_id = request . match_info [ " project_id " ] )
2015-03-07 05:08:00 +02:00
yield from vm . adapter_remove_nio_binding ( int ( request . match_info [ " adapter_number " ] ) )
2015-02-19 20:43:45 +02:00
response . set_status ( 204 )
2015-02-20 15:39:13 +02:00
@classmethod
@Route.get (
2015-02-20 17:54:23 +02:00
r " /qemu/binaries " ,
2015-02-20 15:39:13 +02:00
status_codes = {
200 : " Success " ,
400 : " Invalid request " ,
404 : " Instance doesn ' t exist "
} ,
description = " Get a list of available Qemu binaries " ,
2016-01-01 02:40:12 +02:00
input = QEMU_BINARY_FILTER_SCHEMA ,
2015-02-20 15:39:13 +02:00
output = QEMU_BINARY_LIST_SCHEMA )
def list_binaries ( request , response ) :
2016-01-01 02:40:12 +02:00
binaries = yield from Qemu . binary_list ( request . json [ " archs " ] if " archs " in request . json else None )
2015-02-20 15:39:13 +02:00
response . json ( binaries )
2015-04-13 15:33:13 +03:00
2015-05-10 20:46:51 +03:00
@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 )
2016-01-01 02:40:12 +02:00
@Route.get (
r " /qemu/capabilities " ,
status_codes = {
200 : " Success "
} ,
description = " Get a list of Qemu capabilities on this server " ,
output = QEMU_CAPABILITY_LIST_SCHEMA
)
def get_capabilities ( request , response ) :
capabilities = { }
kvms = Qemu . get_kvm_archs ( )
if kvms :
capabilities [ " kvm " ] = kvms
response . json ( capabilities )
2015-07-27 20:18:36 +03:00
@classmethod
@Route.post (
r " /qemu/img " ,
status_codes = {
201 : " Image created " ,
} ,
2015-07-28 18:50:57 +03:00
description = " Create a Qemu image " ,
input = QEMU_IMAGE_CREATE_SCHEMA
2015-07-27 20:18:36 +03:00
)
def create_img ( request , response ) :
qemu_img = request . json . pop ( " qemu_img " )
path = request . json . pop ( " path " )
2015-07-28 17:05:48 +03:00
if os . path . isabs ( path ) :
config = Config . instance ( )
if config . get_section_config ( " Server " ) . getboolean ( " local " , False ) is False :
response . set_status ( 403 )
return
2015-07-27 20:18:36 +03:00
yield from Qemu . instance ( ) . create_disk ( qemu_img , path , request . json )
response . set_status ( 201 )
2015-04-13 15:33:13 +03:00
@Route.get (
r " /qemu/vms " ,
status_codes = {
200 : " List of Qemu images retrieved " ,
} ,
description = " Retrieve the list of Qemu images " ,
2015-10-05 12:07:15 +03:00
output = VM_LIST_IMAGES_SCHEMA )
2015-04-13 15:33:13 +03:00
def list_vms ( request , response ) :
qemu_manager = Qemu . instance ( )
vms = yield from qemu_manager . list_images ( )
response . set_status ( 200 )
response . json ( vms )
2015-04-24 11:15:23 +03:00
@Route.post (
2015-10-05 21:12:20 +03:00
r " /qemu/vms/ { path:.+} " ,
2015-04-24 11:15:23 +03:00
status_codes = {
204 : " Image uploaded " ,
} ,
raw = True ,
description = " Upload Qemu image. " )
def upload_vm ( request , response ) :
qemu_manager = Qemu . instance ( )
2015-10-05 12:07:15 +03:00
yield from qemu_manager . write_image ( request . match_info [ " path " ] , request . content )
2015-04-24 11:15:23 +03:00
response . set_status ( 204 )