virt-install: Move --install kernel/initrd handling to installtreemedia

This essentially gives us kernel upload, http access, and initrd inject
for free, and ensures that the source file is kept in pristine shape
This commit is contained in:
Cole Robinson 2019-06-12 17:25:56 -04:00
parent b746d919a9
commit 9cbe5f9742
5 changed files with 106 additions and 62 deletions

View File

@ -6,7 +6,8 @@
<os>
<type arch="i686" machine="pc">hvm</type>
<loader readonly="yes" type="pflash">/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd</loader>
<boot dev="network"/>
<kernel>/TESTSUITE_KERNEL_PATH</kernel>
<initrd>/TESTSUITE_INITRD_PATH</initrd>
</os>
<features>
<acpi/>
@ -62,7 +63,7 @@
<os>
<type arch="i686" machine="pc">hvm</type>
<loader readonly="yes" type="pflash">/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd</loader>
<boot dev="network"/>
<boot dev="hd"/>
</os>
<features>
<acpi/>

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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)