1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-30 18:50:58 +00:00

Reinstate qemu-img rebase

This allows importing portable projects with images and/or snapshots,
regression introduced by #1927.

Fixes #1955
This commit is contained in:
Brian Candler 2021-09-04 12:11:50 +01:00
parent 1eaabd6b25
commit 21e9f38437
2 changed files with 45 additions and 33 deletions

View File

@ -1680,24 +1680,14 @@ class QemuVM(BaseNode):
async def _create_linked_clone(self, disk_name, disk_image, disk): async def _create_linked_clone(self, disk_name, disk_image, disk):
try: try:
qemu_img_path = self._get_qemu_img() qemu_img_path = self._get_qemu_img()
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk] backing_options, base_qcow2 = Qcow2.backing_options(disk_image)
try: if base_qcow2 and base_qcow2.crypt_method:
base_qcow2 = Qcow2(disk_image) # Workaround for https://gitlab.com/qemu-project/qemu/-/issues/441
if base_qcow2.crypt_method: # (we have to pass -u and the size). Also embed secret name.
# Workaround for https://gitlab.com/qemu-project/qemu/-/issues/441 command = [qemu_img_path, "create", "-b", backing_options,
# Also embed a secret name so it doesn't have to be passed to qemu -drive ... "-f", "qcow2", "-u", disk, str(base_qcow2.size)]
options = { else:
"encrypt.key-secret": os.path.basename(disk_image), command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk]
"driver": "qcow2",
"file": {
"driver": "file",
"filename": disk_image,
},
}
command = [qemu_img_path, "create", "-b", "json:"+json.dumps(options, separators=(',', ':')),
"-f", "qcow2", "-u", disk, str(base_qcow2.size)]
except Qcow2Error:
pass # non-qcow2 base images are acceptable (e.g. vmdk, raw image)
retcode = await self._qemu_img_exec(command) retcode = await self._qemu_img_exec(command)
if retcode: if retcode:
@ -1880,10 +1870,13 @@ class QemuVM(BaseNode):
# create the disk # create the disk
await self._create_linked_clone(disk_name, disk_image, disk) await self._create_linked_clone(disk_name, disk_image, disk)
else: else:
# The disk exists we check if the clone works # Rebase the image. This is in case the base image moved to a different directory,
# which will be the case if we imported a portable project. This uses
# get_abs_image_path(hdX_disk_image) and ignores the old base path embedded
# in the qcow2 file itself.
try: try:
qcow2 = Qcow2(disk) qcow2 = Qcow2(disk)
await qcow2.validate(qemu_img_path) await qcow2.rebase(qemu_img_path, disk_image)
except (Qcow2Error, OSError) as e: except (Qcow2Error, OSError) as e:
raise QemuError("Could not use qcow2 disk image '{}' for {}: {}".format(disk_image, disk_name, e)) raise QemuError("Could not use qcow2 disk image '{}' for {}: {}".format(disk_image, disk_name, e))

View File

@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import os import os
import asyncio import asyncio
import struct import struct
@ -88,6 +89,35 @@ class Qcow2:
return None return None
return path return path
@staticmethod
def backing_options(base_image):
"""
If the base_image is encrypted qcow2, return options for the upper layer
which include a secret name (equal to the basename)
:param base_image: Path to the base file (which may or may not be qcow2)
:returns: (base image string, Qcow2 object representing base image or None)
"""
try:
base_qcow2 = Qcow2(base_image)
if base_qcow2.crypt_method:
# Embed a secret name so it doesn't have to be passed to qemu -drive ...
options = {
"encrypt.key-secret": os.path.basename(base_image),
"driver": "qcow2",
"file": {
"driver": "file",
"filename": base_image,
},
}
return ("json:"+json.dumps(options, separators=(',', ':')), base_qcow2)
else:
return (base_image, base_qcow2)
except Qcow2Error:
return (base_image, None) # non-qcow2 base images are acceptable (e.g. vmdk, raw image)
async def rebase(self, qemu_img, base_image): async def rebase(self, qemu_img, base_image):
""" """
Rebase a linked clone in order to use the correct disk Rebase a linked clone in order to use the correct disk
@ -98,21 +128,10 @@ class Qcow2:
if not os.path.exists(base_image): if not os.path.exists(base_image):
raise FileNotFoundError(base_image) raise FileNotFoundError(base_image)
command = [qemu_img, "rebase", "-u", "-b", base_image, self._path] backing_options, _ = Qcow2.backing_options(base_image)
command = [qemu_img, "rebase", "-u", "-b", backing_options, self._path]
process = await asyncio.create_subprocess_exec(*command) process = await asyncio.create_subprocess_exec(*command)
retcode = await process.wait() retcode = await process.wait()
if retcode != 0: if retcode != 0:
raise Qcow2Error("Could not rebase the image") raise Qcow2Error("Could not rebase the image")
self._reload() self._reload()
async def validate(self, qemu_img):
"""
Run qemu-img info to validate the file and its backing images
:param qemu_img: Path to the qemu-img binary
"""
command = [qemu_img, "info", "--backing-chain", self._path]
process = await asyncio.create_subprocess_exec(*command)
retcode = await process.wait()
if retcode != 0:
raise Qcow2Error("Could not validate the image")