mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 17:28:08 +00:00
Merge pull request #1927 from candlerb/candlerb/1921
Support cloning of encrypted qcow2 base image files
This commit is contained in:
commit
b68f7a78a7
@ -1681,6 +1681,24 @@ class QemuVM(BaseNode):
|
|||||||
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]
|
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk]
|
||||||
|
try:
|
||||||
|
base_qcow2 = Qcow2(disk_image)
|
||||||
|
if base_qcow2.crypt_method:
|
||||||
|
# Workaround for https://gitlab.com/qemu-project/qemu/-/issues/441
|
||||||
|
# Also embed a secret name so it doesn't have to be passed to qemu -drive ...
|
||||||
|
options = {
|
||||||
|
"encrypt.key-secret": os.path.basename(disk_image),
|
||||||
|
"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:
|
||||||
stdout = self.read_qemu_img_stdout()
|
stdout = self.read_qemu_img_stdout()
|
||||||
@ -1851,6 +1869,7 @@ class QemuVM(BaseNode):
|
|||||||
log.warning("Qemu image {} is corrupted".format(disk_image))
|
log.warning("Qemu image {} is corrupted".format(disk_image))
|
||||||
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
||||||
self.project.emit("log.warning", {"message": "Qemu image '{}' is corrupted and could not be fixed".format(disk_image)})
|
self.project.emit("log.warning", {"message": "Qemu image '{}' is corrupted and could not be fixed".format(disk_image)})
|
||||||
|
# ignore retcode == 1. One reason is that the image is encrypted and there is no encrypt.key-secret available
|
||||||
except (OSError, subprocess.SubprocessError) as e:
|
except (OSError, subprocess.SubprocessError) as e:
|
||||||
stdout = self.read_qemu_img_stdout()
|
stdout = self.read_qemu_img_stdout()
|
||||||
raise QemuError("Could not check '{}' disk image: {}\n{}".format(disk_name, e, stdout))
|
raise QemuError("Could not check '{}' disk image: {}\n{}".format(disk_name, e, stdout))
|
||||||
@ -1864,9 +1883,9 @@ class QemuVM(BaseNode):
|
|||||||
# The disk exists we check if the clone works
|
# The disk exists we check if the clone works
|
||||||
try:
|
try:
|
||||||
qcow2 = Qcow2(disk)
|
qcow2 = Qcow2(disk)
|
||||||
await qcow2.rebase(qemu_img_path, disk_image)
|
await qcow2.validate(qemu_img_path)
|
||||||
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))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
disk = disk_image
|
disk = disk_image
|
||||||
|
@ -58,11 +58,12 @@ class Qcow2:
|
|||||||
# uint64_t snapshots_offset;
|
# uint64_t snapshots_offset;
|
||||||
# } QCowHeader;
|
# } QCowHeader;
|
||||||
|
|
||||||
struct_format = ">IIQi"
|
struct_format = ">IIQiiQi"
|
||||||
with open(self._path, 'rb') as f:
|
with open(self._path, 'rb') as f:
|
||||||
content = f.read(struct.calcsize(struct_format))
|
content = f.read(struct.calcsize(struct_format))
|
||||||
try:
|
try:
|
||||||
self.magic, self.version, self.backing_file_offset, self.backing_file_size = struct.unpack_from(struct_format, content)
|
(self.magic, self.version, self.backing_file_offset, self.backing_file_size,
|
||||||
|
self.cluster_bits, self.size, self.crypt_method) = struct.unpack_from(struct_format, content)
|
||||||
|
|
||||||
except struct.error:
|
except struct.error:
|
||||||
raise Qcow2Error("Invalid file header for {}".format(self._path))
|
raise Qcow2Error("Invalid file header for {}".format(self._path))
|
||||||
@ -103,3 +104,15 @@ class Qcow2:
|
|||||||
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")
|
||||||
|
Loading…
Reference in New Issue
Block a user