2013-03-18 01:06:52 +04:00
#
# Common code for all guests
#
2013-10-28 00:59:46 +04:00
# Copyright 2006-2009, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Jeremy Katz <katzj@redhat.com>
#
2018-03-20 22:00:02 +03:00
# This work is licensed under the GNU GPLv2.
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
import os
import logging
2018-03-20 19:27:37 +03:00
from . devices import DeviceDisk
2014-09-12 23:59:22 +04:00
from . osxml import OSXML
2013-03-18 01:06:52 +04:00
2013-07-17 15:53:47 +04:00
class Installer ( object ) :
2013-03-18 01:06:52 +04:00
"""
Installer classes attempt to encapsulate all the parameters needed
to ' install ' a guest : essentially , booting the guest with the correct
media for the OS install phase ( if there is one ) , and setting up the
guest to boot to the correct media for all subsequent runs .
Some of the actual functionality :
- Determining what type of install media has been requested , and
representing it correctly to the Guest
- Fetching install kernel / initrd or boot . iso from a URL
- Setting the boot device as appropriate depending on whether we
are booting into an OS install , or booting post - install
Some of the information that the Installer needs to know to accomplish
this :
- Install media location ( could be a URL , local path , . . . )
- Virtualization type ( parameter ' os_type ' ) ( ' xen ' , ' hvm ' , etc . )
- Hypervisor name ( parameter ' type ' ) ( ' qemu ' , ' kvm ' , ' xen ' , etc . )
- Guest architecture ( ' i686 ' , ' x86_64 ' )
"""
2013-04-15 00:31:18 +04:00
_has_install_phase = True
2013-07-17 15:53:47 +04:00
def __init__ ( self , conn ) :
self . conn = conn
2013-03-18 01:06:52 +04:00
self . _location = None
2014-01-12 00:28:21 +04:00
self . cdrom = False
2014-09-11 05:33:50 +04:00
self . livecd = False
2016-03-18 05:28:17 +03:00
self . extraargs = [ ]
2013-07-17 04:39:24 +04:00
self . initrd_injections = [ ]
2013-07-17 04:05:24 +04:00
self . _install_kernel = None
self . _install_initrd = None
2013-03-18 01:06:52 +04:00
2013-07-17 00:47:08 +04:00
self . _tmpfiles = [ ]
self . _tmpvols = [ ]
2013-03-18 01:06:52 +04:00
2013-07-17 15:53:47 +04:00
#########################
# Properties properties #
#########################
2013-03-18 01:06:52 +04:00
def get_location ( self ) :
return self . _location
def set_location ( self , val ) :
2013-07-17 02:12:13 +04:00
self . _location = self . _validate_location ( val )
2013-03-18 01:06:52 +04:00
location = property ( get_location , set_location )
2013-07-17 02:12:13 +04:00
###################
# Private helpers #
###################
2013-03-18 01:06:52 +04:00
def _build_boot_order ( self , isinstall , guest ) :
2013-07-17 15:53:47 +04:00
bootorder = [ self . _get_bootdev ( isinstall , guest ) ]
2013-03-18 01:06:52 +04:00
# If guest has an attached disk, always have 'hd' in the boot
# list, so disks are marked as bootable/installable (needed for
# windows virtio installs, and booting local disk from PXE)
for disk in guest . get_devices ( " disk " ) :
if disk . device == disk . DEVICE_DISK :
2013-07-17 15:53:47 +04:00
bootdev = " hd "
2013-03-18 01:06:52 +04:00
if bootdev not in bootorder :
bootorder . append ( bootdev )
break
return bootorder
2015-04-07 23:38:52 +03:00
def alter_bootconfig ( self , guest , isinstall ) :
2013-03-18 01:06:52 +04:00
"""
Generate the portion of the guest xml that determines boot devices
and parameters . ( typically the < os > < / os > block )
2018-02-14 15:17:31 +03:00
: param guest : Guest instance we are installing
: param isinstall : Whether we want xml for the ' install ' phase or the
2013-03-18 01:06:52 +04:00
' post-install ' phase .
"""
if isinstall and not self . has_install_phase ( ) :
return
2017-04-05 01:22:15 +03:00
bootorder = guest . os . bootorder
if isinstall or not bootorder :
2014-02-09 22:36:12 +04:00
# Per device <boot order> is not compatible with os/boot.
if not any ( d . boot . order for d in guest . get_all_devices ( ) ) :
2017-04-05 01:22:15 +03:00
bootorder = self . _build_boot_order ( isinstall , guest )
2013-03-18 01:06:52 +04:00
2017-04-05 01:22:15 +03:00
guest . os . bootorder = bootorder
2013-07-17 15:53:47 +04:00
if not isinstall :
return
2013-07-17 04:05:24 +04:00
2013-07-17 15:53:47 +04:00
if self . _install_kernel :
2015-04-07 23:38:52 +03:00
guest . os . kernel = self . _install_kernel
2013-07-17 15:53:47 +04:00
if self . _install_initrd :
2015-04-07 23:38:52 +03:00
guest . os . initrd = self . _install_initrd
2014-01-12 00:28:21 +04:00
if self . extraargs :
2016-03-18 05:28:17 +03:00
guest . os . kernel_args = " " . join ( self . extraargs )
2013-03-18 01:06:52 +04:00
2013-07-17 02:12:13 +04:00
##########################
# Internal API overrides #
##########################
def _get_bootdev ( self , isinstall , guest ) :
raise NotImplementedError ( " Must be implemented in subclass " )
def _validate_location ( self , val ) :
return val
2014-09-07 21:35:34 +04:00
def _prepare ( self , guest , meter ) :
2013-07-17 02:12:13 +04:00
ignore = guest
ignore = meter
##############
# Public API #
##############
def scratchdir_required ( self ) :
"""
Returns true if scratchdir is needed for the passed install parameters .
Apps can use this to determine if they should attempt to ensure
scratchdir permissions are adequate
"""
return False
2013-03-18 01:06:52 +04:00
def has_install_phase ( self ) :
"""
Return True if the requested setup is actually installing an OS
into the guest . Things like LiveCDs , Import , or a manually specified
bootorder do not have an install phase .
"""
2013-04-15 00:31:18 +04:00
return self . _has_install_phase
2013-03-18 01:06:52 +04:00
2015-06-06 23:56:03 +03:00
def needs_cdrom ( self ) :
"""
If this installer uses cdrom media , so it needs a cdrom device
attached to the VM
"""
return False
def cdrom_path ( self ) :
"""
Return the cdrom path needed for needs_cdrom ( ) installs
"""
return None
2013-03-18 01:06:52 +04:00
def cleanup ( self ) :
"""
Remove any temporary files retrieved during installation
"""
for f in self . _tmpfiles :
2018-03-02 11:01:23 +03:00
logging . debug ( " Removing %s " , str ( f ) )
2013-03-18 01:06:52 +04:00
os . unlink ( f )
for vol in self . _tmpvols :
logging . debug ( " Removing volume ' %s ' " , vol . name ( ) )
vol . delete ( 0 )
self . _tmpvols = [ ]
self . _tmpfiles = [ ]
2014-09-07 21:35:34 +04:00
def prepare ( self , guest , meter ) :
2013-07-17 02:12:13 +04:00
self . cleanup ( )
2013-09-26 21:04:28 +04:00
try :
2014-09-07 21:35:34 +04:00
self . _prepare ( guest , meter )
2017-07-24 11:26:48 +03:00
except Exception :
2013-09-26 21:04:28 +04:00
self . cleanup ( )
raise
2013-07-17 02:12:13 +04:00
2013-09-26 21:04:28 +04:00
def check_location ( self , guest ) :
2013-03-18 01:06:52 +04:00
"""
2013-07-17 02:12:13 +04:00
Validate self . location seems to work . This will might hit the
network so we don ' t want to do it on demand.
2013-03-18 01:06:52 +04:00
"""
2013-09-26 21:04:28 +04:00
ignore = guest
2013-07-17 02:12:13 +04:00
return True
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def detect_distro ( self , guest ) :
2013-03-18 01:06:52 +04:00
"""
Attempt to detect the distro for the Installer ' s ' location ' . If
an error is encountered in the detection process ( or if detection
2014-09-07 19:57:04 +04:00
is not relevant for the Installer type ) , None is returned .
2013-03-18 01:06:52 +04:00
2018-02-14 15:17:31 +03:00
: returns : distro variant string , or None
2013-03-18 01:06:52 +04:00
"""
2013-09-26 21:04:28 +04:00
ignore = guest
2014-09-07 19:57:04 +04:00
logging . debug ( " distro detection not available for this installer. " )
return None
2013-03-18 01:06:52 +04:00
2013-04-12 17:51:26 +04:00
2013-03-18 01:06:52 +04:00
class ContainerInstaller ( Installer ) :
2013-04-15 00:31:18 +04:00
_has_install_phase = False
2013-03-18 01:06:52 +04:00
def _get_bootdev ( self , isinstall , guest ) :
ignore = isinstall
ignore = guest
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_HARDDISK
2013-07-16 21:31:58 +04:00
class PXEInstaller ( Installer ) :
def _get_bootdev ( self , isinstall , guest ) :
2013-07-17 15:53:47 +04:00
bootdev = OSXML . BOOT_DEVICE_NETWORK
2013-07-16 21:31:58 +04:00
if ( not isinstall and
[ d for d in guest . get_devices ( " disk " ) if
d . device == d . DEVICE_DISK ] ) :
# If doing post-install boot and guest has an HD attached
2013-07-17 15:53:47 +04:00
bootdev = OSXML . BOOT_DEVICE_HARDDISK
2013-07-16 21:31:58 +04:00
return bootdev
class ImportInstaller ( Installer ) :
_has_install_phase = False
# Private methods
def _get_bootdev ( self , isinstall , guest ) :
disks = guest . get_devices ( " disk " )
if not disks :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_HARDDISK
2013-07-16 21:31:58 +04:00
return self . _disk_to_bootdev ( disks [ 0 ] )
def _disk_to_bootdev ( self , disk ) :
2018-03-20 19:18:35 +03:00
if disk . device == DeviceDisk . DEVICE_DISK :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_HARDDISK
2018-03-20 19:18:35 +03:00
elif disk . device == DeviceDisk . DEVICE_CDROM :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_CDROM
2018-03-20 19:18:35 +03:00
elif disk . device == DeviceDisk . DEVICE_FLOPPY :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_FLOPPY
2013-07-16 21:31:58 +04:00
else :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_HARDDISK