Hello all, Here are a couple of patches for ganeti that make it possible to pass a cdrom_image_path that is an HTTP(s)/FTP(s) url. The original patch was written by Apollon Oikonomopoulos more than 2 years ago and minor modifications were made by Christos Stavrakakis in order for the patches to apply cleanly to 2.7+ versions. There were occasional hiccups when using this feature with an old kvm version (0.12) but with version 1.1.2+ we haven't noticed any problems so far.
We are currently using this functionality on our ganetimgr[1] frontend with ganeti versions 2.6, 2.7 and 2.9 to give our users the ability to boot their VMs without an OS and then install a custom ISO of their choice. We (at GRNET) have been sitting on this patch for quite a while and we hope it's time it gets merged upstream. Regards, -- George Kargiotakis GRNET NOC [1] http://ganetimgr.readthedocs.org/en/wheezy/
commit 9fe0df635fb34ad1eb69fbb62bd91061f9154e6a Author: Christos Stavrakakis <[email protected]> Date: Tue Oct 29 12:10:54 2013 +0200 kvm: check that the ISO image is there if it's a URL Perform a simple urllib2 check on ISO images specified as URL before instance start, so as to work around qemu bug #597575 [1]. [1] https://bugs.launchpad.net/qemu/+bug/597575 Signed-off-by: Apollon Oikonomopoulos <[email protected]> Signed-off-by: Christos Stavrakakis <[email protected]> Index: ganeti-2.9.2/lib/hypervisor/hv_kvm.py =================================================================== --- ganeti-2.9.2.orig/lib/hypervisor/hv_kvm.py 2014-01-09 17:27:18.000000000 +0200 +++ ganeti-2.9.2/lib/hypervisor/hv_kvm.py 2014-01-09 17:27:18.000000000 +0200 @@ -34,6 +34,7 @@ import struct import fcntl import shutil +import urllib2 import socket import stat import StringIO @@ -164,6 +165,24 @@ return (ifname, tapfd) +def _CheckUrl(url): + """Check if a given URL exists on the server + + """ + req = urllib2.Request(url) + + # XXX: ugly but true + req.get_method = lambda: "HEAD" + + try: + resp = urllib2.urlopen(req) + except urllib2.URLError: + return False + + del resp + return True + + class QmpMessage: """QEMU Messaging Protocol (QMP) message. @@ -1161,7 +1180,13 @@ iso_image = hvp[constants.HV_CDROM_IMAGE_PATH] if iso_image: options = ",media=cdrom" - if not re.match(r'(https?|ftps?)://', iso_image): + if re.match(r'(https?|ftps?)://', iso_image): + # Check that the iso image is really there + # See https://bugs.launchpad.net/qemu/+bug/597575 + if not _CheckUrl(iso_image): + raise errors.HypervisorError("ISO image %s is not accessible" % + iso_image) + else: options = "%s,format=raw" % options # set cdrom 'if' type if boot_cdrom:
commit abfdf963d6727fd031d97864956511a30087dd11 Author: Christos Stavrakakis <[email protected]> Date: Tue Oct 29 12:09:33 2013 +0200 Allow KVM to boot from HTTP New versions of KVM support booting from HTTP-hosted ISO images, via libcurl. This patch adds a proper check to allow defining either a sane, absolute path or an HTTP URL as an iso image path. Remove "format=raw" from the cdrom device options when iso_image starts with "(https?|ftps?)://", to allow latest KVM versions (qemu-kvm) to boot from HTTP-hosted iso images. Added FILE_OR_URL check to allow booting from HTTP (kvm). Signed-off-by: Apollon Oikonomopoulos <[email protected]> Signed-off-by: Christos Stavrakakis <[email protected]> diff --git a/lib/hypervisor/hv_base.py b/lib/hypervisor/hv_base.py index c723a4d..a6e0b46 100644 --- a/lib/hypervisor/hv_base.py +++ b/lib/hypervisor/hv_base.py @@ -83,6 +83,13 @@ def _IsMultiCpuMaskWellFormed(cpu_mask): _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path", os.path.isfile, "not found or not a file") +# must be a file or a URL +_FILE_OR_URL_CHECK = (utils.IsNormAbsPathOrURL, + "must be an absolute normalized path or a URL", + lambda x: os.path.isfile(x) or + re.match(r'(https?|ftps?)://', x), + "not found or not a file or URL") + # must be a directory _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path", os.path.isdir, "not found or not a directory") @@ -108,6 +115,8 @@ _NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None) # nice wrappers for users REQ_FILE_CHECK = (True, ) + _FILE_CHECK OPT_FILE_CHECK = (False, ) + _FILE_CHECK +REQ_FILE_OR_URL_CHECK = (True, ) + _FILE_OR_URL_CHECK +OPT_FILE_OR_URL_CHECK = (False, ) + _FILE_OR_URL_CHECK REQ_DIR_CHECK = (True, ) + _DIR_CHECK OPT_DIR_CHECK = (False, ) + _DIR_CHECK REQ_NET_PORT_CHECK = (True, ) + _NET_PORT_CHECK diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 54bd557..01a3c52 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -477,7 +477,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK, constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK, constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK, - constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, + constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK, constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK, constants.HV_BOOT_ORDER: hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES), @@ -1130,7 +1130,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): iso_image = hvp[constants.HV_CDROM_IMAGE_PATH] if iso_image: - options = ",format=raw,media=cdrom" + options = ",media=cdrom" + if not re.match(r'(https?|ftps?)://', iso_image): + options = "%s,format=raw" % options # set cdrom 'if' type if boot_cdrom: actual_cdrom_type = constants.HT_DISK_IDE diff --git a/lib/utils/io.py b/lib/utils/io.py index f9675a8..47c7749 100644 --- a/lib/utils/io.py +++ b/lib/utils/io.py @@ -23,6 +23,7 @@ """ import os +import re import logging import shutil import tempfile @@ -667,6 +668,13 @@ def IsBelowDir(root, other_path): return os.path.commonprefix([prepared_root, norm_other]) == prepared_root +def IsNormAbsPathOrURL(path): + """Check whether a path is absolute and normalized, or an HTTP URL. + + """ + return IsNormAbsPath(path) or re.match(r'(https?|ftps?)://', path) + + def PathJoin(*args): """Safe-join a list of path components.
