diff --git a/tests/cli-test-xml/compare/virt-install-kvm-i686-uefi.xml b/tests/cli-test-xml/compare/virt-install-kvm-i686-uefi.xml
index 7611430a..f58f39d2 100644
--- a/tests/cli-test-xml/compare/virt-install-kvm-i686-uefi.xml
+++ b/tests/cli-test-xml/compare/virt-install-kvm-i686-uefi.xml
@@ -6,7 +6,8 @@
hvm
/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd
-
+ /TESTSUITE_KERNEL_PATH
+ /TESTSUITE_INITRD_PATH
@@ -62,7 +63,7 @@
hvm
/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd
-
+
diff --git a/tests/clitest.py b/tests/clitest.py
index 1148c96f..7ee7093a 100644
--- a/tests/clitest.py
+++ b/tests/clitest.py
@@ -909,7 +909,7 @@ c.add_compare("--disk none --location %(ISO-NO-OS)s,kernel=frib.img,initrd=/frob
c.add_compare("--disk %(EXISTIMG1)s --location %(ISOTREE)s --nonetworks", "location-iso", prerun_check=missing_isoinfo) # Using --location iso mounting
c.add_compare("--disk %(EXISTIMG1)s --cdrom %(ISOLABEL)s", "cdrom-centos-label") # Using --cdrom with centos CD label, should use virtio etc.
c.add_compare("--disk %(EXISTIMG1)s --install bootdev=network --os-variant rhel5.4", "kvm-rhel5") # RHEL5 defaults
-c.add_compare("--disk %(EXISTIMG1)s --install kernel=./foo,initrd=./bar,kernel_args='foo bar' --os-variant rhel6.4", "kvm-rhel6") # RHEL6 defaults
+c.add_compare("--disk %(EXISTIMG1)s --install kernel=%(ISO-WIN7)s,initrd=%(ISOLABEL)s,kernel_args='foo bar' --os-variant rhel6.4", "kvm-rhel6") # RHEL6 defaults. ISO paths are just to point at existing files
c.add_compare("--disk %(EXISTIMG1)s --location https://example.com --install kernel_args='test overwrite',kernel_args_overwrite=yes --os-variant rhel7.0", "kvm-rhel7", prerun_check=has_old_osinfo) # RHEL7 defaults
c.add_compare("--connect " + utils.URIs.kvm_nodomcaps + " --disk %(EXISTIMG1)s --pxe --os-variant rhel7.0", "kvm-cpu-default-fallback", prerun_check=has_old_osinfo) # No domcaps, so mode=host-model isn't safe, so we fallback to host-model-only
c.add_compare("--connect " + utils.URIs.kvm_nodomcaps + " --cpu host-copy --disk none --pxe", "kvm-hostcopy-fallback") # No domcaps so need to use capabilities for CPU host-copy
@@ -917,7 +917,7 @@ c.add_compare("--disk %(EXISTIMG1)s --pxe --os-variant centos7.0", "kvm-centos7"
c.add_compare("--disk %(EXISTIMG1)s --pxe --os-variant centos7.0", "kvm-centos7", prerun_check=has_old_osinfo) # Centos 7 defaults
c.add_compare("--disk %(EXISTIMG1)s --cdrom %(EXISTIMG2)s --os-variant win10", "kvm-win10", prerun_check=has_old_osinfo) # win10 defaults
c.add_compare("--os-variant win7 --cdrom %(EXISTIMG2)s --boot loader_type=pflash,loader=CODE.fd,nvram_template=VARS.fd --disk %(EXISTIMG1)s", "win7-uefi", prerun_check=has_old_osinfo) # no HYPER-V with UEFI
-c.add_compare("--arch i686 --boot uefi --pxe --disk none", "kvm-i686-uefi") # i686 uefi
+c.add_compare("--arch i686 --boot uefi --install kernel=http://example.com/httpkernel,initrd=ftp://example.com/ftpinitrd --disk none", "kvm-i686-uefi") # i686 uefi. piggy back it for --install testing too
c.add_compare("--machine q35 --cdrom %(EXISTIMG2)s --disk %(EXISTIMG1)s", "q35-defaults") # proper q35 disk defaults
c.add_compare("--disk size=1 --os-variant openbsd4.9", "openbsd-defaults") # triggers net fallback scenario
c.add_compare("--connect " + utils.URIs.kvm_remote + " --import --disk %(EXISTIMG1)s --os-variant fedora21 --pm suspend_to_disk=yes", "f21-kvm-remote", prerun_check=has_old_osinfo)
diff --git a/virtinst/installer.py b/virtinst/installer.py
index 2f5087fd..547336ee 100644
--- a/virtinst/installer.py
+++ b/virtinst/installer.py
@@ -51,8 +51,6 @@ class Installer(object):
self.autostart = False
self._install_bootdev = install_bootdev
- self._install_kernel = None
- self._install_initrd = None
self._install_kernel_args = install_kernel_args
self._install_cdrom_device_added = False
self._unattended_install_cdrom_device = None
@@ -61,18 +59,17 @@ class Installer(object):
self._unattended_data = None
self._treemedia = None
+ self._treemedia_bootconfig = None
self._cdrom = None
if cdrom:
cdrom = InstallerTreeMedia.validate_path(self.conn, cdrom)
self._cdrom = cdrom
self._install_bootdev = "cdrom"
- elif location or location_kernel or location_initrd:
+ elif (location or location_kernel or location_initrd or
+ install_kernel or install_initrd):
self._treemedia = InstallerTreeMedia(self.conn, location,
- location_kernel, location_initrd)
- elif install_kernel or install_initrd:
- self._install_kernel = os.path.realpath(install_kernel)
- self._install_initrd = os.path.realpath(install_initrd)
- self._install_bootdev = None
+ location_kernel, location_initrd,
+ install_kernel, install_initrd)
###################
@@ -162,6 +159,26 @@ class Installer(object):
not guest.os.kernel and
not any([d.boot.order for d in guest.devices.get_all()]))
+ def _alter_treemedia_bootconfig(self, guest):
+ if not self._treemedia:
+ return
+
+ kernel, initrd, kernel_args = self._treemedia_bootconfig
+ if kernel_args:
+ self.extra_args.append(kernel_args)
+
+ if kernel:
+ guest.os.kernel = (self.conn.in_testsuite() and
+ "/TESTSUITE_KERNEL_PATH" or kernel)
+ if initrd:
+ guest.os.initrd = (self.conn.in_testsuite() and
+ "/TESTSUITE_INITRD_PATH" or initrd)
+
+ if self._install_kernel_args:
+ guest.os.kernel_args = self._install_kernel_args
+ elif self.extra_args:
+ guest.os.kernel_args = " ".join(self.extra_args)
+
def _alter_bootconfig(self, guest):
"""
Generate the portion of the guest xml that determines boot devices
@@ -170,18 +187,7 @@ class Installer(object):
:param guest: Guest instance we are installing
"""
guest.on_reboot = "destroy"
-
- if self._install_kernel:
- guest.os.kernel = (self.conn.in_testsuite() and
- "/TESTSUITE_KERNEL_PATH" or self._install_kernel)
- if self._install_initrd:
- guest.os.initrd = (self.conn.in_testsuite() and
- "/TESTSUITE_INITRD_PATH" or self._install_initrd)
-
- if self._install_kernel_args:
- guest.os.kernel_args = self._install_kernel_args
- elif self.extra_args:
- guest.os.kernel_args = " ".join(self.extra_args)
+ self._alter_treemedia_bootconfig(guest)
bootdev = self._install_bootdev
if bootdev and self._can_set_guest_bootorder(guest):
@@ -248,12 +254,8 @@ class Installer(object):
unattended_script = self._prepare_unattended_script(guest, meter)
if self._treemedia:
- k, i, a = self._treemedia.prepare(guest, meter,
+ self._treemedia_bootconfig = self._treemedia.prepare(guest, meter,
unattended_script)
- self._install_kernel = k
- self._install_initrd = i
- if a:
- self.extra_args.append(a)
elif unattended_script:
self._prepare_unattended_data(guest, unattended_script)
@@ -346,8 +348,6 @@ class Installer(object):
return False
return bool(self._cdrom or
self._install_bootdev or
- self._install_kernel or
- self._install_initrd or
self._treemedia)
def detect_distro(self, guest):
diff --git a/virtinst/installertreemedia.py b/virtinst/installertreemedia.py
index ff85debf..1e57a7aa 100644
--- a/virtinst/installertreemedia.py
+++ b/virtinst/installertreemedia.py
@@ -19,7 +19,8 @@ from .osdict import OSDB
# Enum of the various install media types we can have
(MEDIA_DIR,
MEDIA_ISO,
- MEDIA_URL) = range(1, 4)
+ MEDIA_URL,
+ MEDIA_KERNEL) = range(1, 5)
def _is_url(url):
@@ -98,11 +99,15 @@ class InstallerTreeMedia(object):
return system_scratchdir # pragma: no cover
- def __init__(self, conn, location, location_kernel, location_initrd):
+ def __init__(self, conn, location, location_kernel, location_initrd,
+ install_kernel, install_initrd):
self.conn = conn
self.location = location
self._location_kernel = location_kernel
self._location_initrd = location_initrd
+ self._install_kernel = install_kernel
+ self._install_initrd = install_initrd
+
self.initrd_injections = []
if location_kernel or location_initrd:
@@ -119,16 +124,21 @@ class InstallerTreeMedia(object):
self._tmpfiles = []
self._tmpvols = []
- self._media_type = MEDIA_ISO
- if (not self.conn.is_remote() and
- os.path.exists(self.location) and
- os.path.isdir(self.location)):
+ if self._install_kernel or self._install_initrd:
+ self._media_type = MEDIA_KERNEL
+ elif (not self.conn.is_remote() and
+ os.path.exists(self.location) and
+ os.path.isdir(self.location)):
self.location = os.path.abspath(self.location)
self._media_type = MEDIA_DIR
elif _is_url(self.location):
self._media_type = MEDIA_URL
+ else:
+ self._media_type = MEDIA_ISO
- if self.conn.is_remote() and not self._media_type == MEDIA_URL:
+ if (self.conn.is_remote() and
+ not self._media_type == MEDIA_URL and
+ not self._media_type == MEDIA_KERNEL):
raise ValueError(_("Cannot access install tree on remote "
"connection: %s") % self.location)
@@ -146,32 +156,44 @@ class InstallerTreeMedia(object):
if not self._cached_fetcher:
scratchdir = InstallerTreeMedia.make_scratchdir(guest)
- self._cached_fetcher = urlfetcher.fetcherForURI(
- self.location, scratchdir, meter)
+ if self._media_type == MEDIA_KERNEL:
+ self._cached_fetcher = urlfetcher.DirectFetcher(
+ None, scratchdir, meter)
+ else:
+ self._cached_fetcher = urlfetcher.fetcherForURI(
+ self.location, scratchdir, meter)
self._cached_fetcher.meter = meter
return self._cached_fetcher
def _get_cached_data(self, guest, fetcher):
- if not self._cached_data:
- has_location_kernel = bool(
- self._location_kernel and self._location_initrd)
+ if self._cached_data:
+ return self._cached_data
+
+ store = None
+ os_variant = None
+ os_media = None
+ kernel_paths = []
+ has_location_kernel = bool(
+ self._location_kernel and self._location_initrd)
+
+ if self._media_type == MEDIA_KERNEL:
+ kernel_paths = [
+ (self._install_kernel, self._install_initrd)]
+ else:
store = urldetect.getDistroStore(guest, fetcher,
skip_error=has_location_kernel)
- os_variant = None
- os_media = None
- kernel_paths = []
- if store:
- kernel_paths = store.get_kernel_paths()
- os_variant = store.get_osdict_info()
- os_media = store.get_os_media()
- if has_location_kernel:
- kernel_paths = [
- (self._location_kernel, self._location_initrd)]
+ if store:
+ kernel_paths = store.get_kernel_paths()
+ os_variant = store.get_osdict_info()
+ os_media = store.get_os_media()
+ if has_location_kernel:
+ kernel_paths = [
+ (self._location_kernel, self._location_initrd)]
- self._cached_data = _LocationData(os_variant, kernel_paths,
- os_media)
+ self._cached_data = _LocationData(os_variant, kernel_paths,
+ os_media)
return self._cached_data
def _prepare_kernel_url(self, guest, cache, fetcher):
diff --git a/virtinst/urlfetcher.py b/virtinst/urlfetcher.py
index 6a5a0bfc..169d4f69 100644
--- a/virtinst/urlfetcher.py
+++ b/virtinst/urlfetcher.py
@@ -119,12 +119,15 @@ class _URLFetcher(object):
return self.location
return os.path.join(self.location, filename)
- def _grabURL(self, filename, fileobj):
+ def _grabURL(self, filename, fileobj, fullurl=None):
"""
Download the filename from self.location, and write contents to
fileobj
"""
- url = self._make_full_url(filename)
+ if fullurl:
+ url = fullurl
+ else:
+ url = self._make_full_url(filename)
try:
urlobj, size = self._grabber(url)
@@ -203,7 +206,7 @@ class _URLFetcher(object):
logging.debug("hasFile(%s) returning %s", url, ret)
return ret
- def acquireFile(self, filename):
+ def acquireFile(self, filename, fullurl=None):
"""
Grab the passed filename from self.location and save it to
a temporary file, returning the temp filename
@@ -217,7 +220,7 @@ class _URLFetcher(object):
dir=self.scratchdir, prefix=prefix, delete=False)
fn = fileobj.name
- self._grabURL(filename, fileobj)
+ self._grabURL(filename, fileobj, fullurl=fullurl)
logging.debug("Saved file to %s", fn)
return fn
except: # noqa
@@ -401,15 +404,33 @@ class _ISOURLFetcher(_URLFetcher):
return url.encode("ascii") in self._cache_file_list
-def fetcherForURI(uri, *args, **kwargs):
+class DirectFetcher(_URLFetcher):
+ def _make_full_url(self, filename):
+ return filename
+
+ def acquireFile(self, filename, fullurl=None):
+ fullurl = filename
+ filename = os.path.basename(filename)
+ fetcher = fetcherForURI(fullurl, self.scratchdir, self.meter, direct=True)
+ return fetcher.acquireFile(filename, fullurl) # pylint: disable=protected-access
+
+ def _hasFile(self, url):
+ return True
+
+ def _grabber(self, url):
+ raise RuntimeError( # pragma: no cover
+ "DirectFetcher shouldn't be used for file access.")
+
+
+def fetcherForURI(uri, scratchdir, meter, direct=False):
if uri.startswith("http://") or uri.startswith("https://"):
fclass = _HTTPURLFetcher
elif uri.startswith("ftp://"):
fclass = _FTPURLFetcher
- elif os.path.isdir(uri):
+ elif direct or os.path.isdir(uri):
# Pointing to a local tree
fclass = _LocalURLFetcher
else:
# Pointing to a path (e.g. iso), or a block device (e.g. /dev/cdrom)
fclass = _ISOURLFetcher
- return fclass(uri, *args, **kwargs)
+ return fclass(uri, scratchdir, meter)