1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-28 11:18:11 +00:00

Allow to resize a Qemu VM disk (extend only).

This commit is contained in:
grossmj 2018-03-26 18:05:49 +07:00
parent 6cec67f79f
commit ff7911bd99
6 changed files with 148 additions and 6 deletions

View File

@ -296,7 +296,7 @@ class Qemu(BaseManager):
raise QemuError("Could not create disk image {}:{}".format(path, e))
@asyncio.coroutine
def resize_disk(self, qemu_img, path, size):
def resize_disk(self, qemu_img, path, extend):
"""
Resize a Qemu disk with qemu-img
@ -305,11 +305,17 @@ class Qemu(BaseManager):
:param size: size
"""
if not os.path.isabs(path):
directory = self.get_images_directory()
os.makedirs(directory, exist_ok=True)
path = os.path.join(directory, os.path.basename(path))
try:
if not os.path.exists(path):
raise QemuError("Qemu image '{}' does not exist".format(path))
command = [qemu_img, "resize", path, "{}M".format(size)]
raise QemuError("Qemu disk '{}' does not exist".format(path))
command = [qemu_img, "resize", path, "+{}M".format(extend)]
process = yield from asyncio.create_subprocess_exec(*command)
yield from process.wait()
log.info("Qemu disk '{}' extended by {} MB".format(path, extend))
except (OSError, subprocess.SubprocessError) as e:
raise QemuError("Could not create disk image {}:{}".format(path, e))
raise QemuError("Could not update disk image {}:{}".format(path, e))

View File

@ -1586,6 +1586,22 @@ class QemuVM(BaseNode):
return options
@asyncio.coroutine
def resize_disk(self, drive_name, extend):
if self.is_running():
raise QemuError("Cannot resize {} while the VM is running".format(drive_name))
if self.linked_clone:
disk_image_path = os.path.join(self.working_dir, "{}_disk.qcow2".format(drive_name))
else:
disk_image_path = getattr(self, "{}_disk_image".format(drive_name))
if not os.path.exists(disk_image_path):
raise QemuError("Disk path '{}' does not exist".format(disk_image_path))
qemu_img_path = self._get_qemu_img()
yield from self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
def _cdrom_option(self):
options = []

View File

@ -35,10 +35,12 @@ from gns3server.schemas.qemu import (
QEMU_CREATE_SCHEMA,
QEMU_UPDATE_SCHEMA,
QEMU_OBJECT_SCHEMA,
QEMU_RESIZE_SCHEMA,
QEMU_BINARY_LIST_SCHEMA,
QEMU_BINARY_FILTER_SCHEMA,
QEMU_CAPABILITY_LIST_SCHEMA,
QEMU_IMAGE_CREATE_SCHEMA
QEMU_IMAGE_CREATE_SCHEMA,
QEMU_IMAGE_UPDATE_SCHEMA
)
@ -163,6 +165,25 @@ class QEMUHandler:
response.set_status(201)
response.json(new_node)
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/resize_disk",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Instance updated",
404: "Instance doesn't exist"
},
description="Resize a Qemu VM disk image",
input=QEMU_RESIZE_SCHEMA)
def resize_disk(request, response):
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
yield from vm.resize_disk(request.json["drive_name"], request.json["extend"])
response.set_status(201)
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/start",
parameters={
@ -458,6 +479,28 @@ class QEMUHandler:
yield from Qemu.instance().create_disk(qemu_img, path, request.json)
response.set_status(201)
@Route.put(
r"/qemu/img",
status_codes={
201: "Image Updated",
},
description="Update a Qemu image",
input=QEMU_IMAGE_UPDATE_SCHEMA
)
def update_img(request, response):
qemu_img = request.json.pop("qemu_img")
path = request.json.pop("path")
if os.path.isabs(path):
config = Config.instance()
if config.get_section_config("Server").getboolean("local", False) is False:
response.set_status(403)
return
if "extend" in request.json:
yield from Qemu.instance().resize_disk(qemu_img, path, request.json.pop("extend"))
response.set_status(201)
@Route.get(
r"/qemu/images",
status_codes={

View File

@ -155,6 +155,23 @@ class ComputeHandler:
res = yield from compute.forward("POST", request.match_info["emulator"], request.match_info["action"], data=request.content)
response.json(res)
@Route.put(
r"/computes/{compute_id}/{emulator}/{action:.+}",
parameters={
"compute_id": "Compute UUID"
},
status_codes={
200: "OK",
404: "Instance doesn't exist"
},
raw=True,
description="Forward call specific to compute node. Read the full compute API for available actions")
def put_forward(request, response):
controller = Controller.instance()
compute = controller.get_compute(request.match_info["compute_id"])
res = yield from compute.forward("PUT", request.match_info["emulator"], request.match_info["action"], data=request.content)
response.json(res)
@Route.get(
r"/computes/{compute_id}",
description="Get a compute server information",

View File

@ -286,7 +286,7 @@ class NodeHandler:
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.reload()
#yield from node.reload()
response.json(node)
response.set_status(201)
@ -347,6 +347,25 @@ class NodeHandler:
response.json(idle)
response.set_status(200)
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/resize_disk",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
201: "Disk image resized",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reload a node instance")
def resize_disk(request, response):
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
yield from node.post("/resize_disk", request.json)
response.set_status(201)
@Route.get(
r"/projects/{project_id}/nodes/{node_id}/files/{path:.+}",
parameters={

View File

@ -641,6 +641,25 @@ QEMU_OBJECT_SCHEMA = {
"status"]
}
QEMU_RESIZE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Resize a disk in a QEMU VM",
"type": "object",
"properties": {
"drive_name": {
"description": "Absolute or relative path of the image",
"enum": ["hda", "hdb", "hdc", "hdd"]
},
"extend": {
"description": "Number of Megabytes to extend the image",
"type": "integer"
},
# TODO: support shrink? (could be dangerous)
},
"required": ["drive_name", "extend"],
"additionalProperties": False
}
QEMU_BINARY_FILTER_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Request validation for a list of QEMU capabilities",
@ -758,3 +777,25 @@ QEMU_IMAGE_CREATE_SCHEMA = {
"required": ["qemu_img", "path", "format", "size"],
"additionalProperties": False
}
QEMU_IMAGE_UPDATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Update an existing QEMU image",
"type": "object",
"properties": {
"qemu_img": {
"description": "Path to the qemu-img binary",
"type": "string"
},
"path": {
"description": "Absolute or relative path of the image",
"type": "string"
},
"extend": {
"description": "Number of Megabytes to extend the image",
"type": "integer"
},
},
"required": ["qemu_img", "path"],
"additionalProperties": False
}