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>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
2013-10-28 00:59:47 +04:00
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2013-03-18 01:06:52 +04:00
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
import os
import logging
2014-09-12 23:59:22 +04:00
from . devicedisk import VirtualDisk
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
2014-01-12 00:28:21 +04:00
self . extraargs = None
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
# Devices created/added during the prepare() stage
self . install_devices = [ ]
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
2013-09-26 21:04:28 +04:00
def _make_cdrom_dev ( self , path , transient = False ) :
2014-09-12 23:59:22 +04:00
dev = VirtualDisk ( self . conn )
2013-07-13 18:09:00 +04:00
dev . path = path
dev . device = dev . DEVICE_CDROM
dev . read_only = True
2014-12-06 04:28:32 +03:00
setattr ( dev , " installer_media " , transient )
2013-09-26 21:04:28 +04:00
2013-07-13 18:09:00 +04:00
dev . validate ( )
return dev
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 )
@param guest : Guest instance we are installing
@type guest : L { Guest }
@param isinstall : Whether we want xml for the ' install ' phase or the
' post-install ' phase .
@type isinstall : C { bool }
"""
if isinstall and not self . has_install_phase ( ) :
return
bootorder = self . _build_boot_order ( isinstall , guest )
2013-07-17 04:05:24 +04:00
2015-04-07 23:38:52 +03:00
if not guest . os . 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 ( ) ) :
2015-04-07 23:38:52 +03:00
guest . os . bootorder = bootorder
2013-03-18 01:06:52 +04:00
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 :
2015-04-07 23:38:52 +03:00
guest . os . kernel_args = 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
def cleanup ( self ) :
"""
Remove any temporary files retrieved during installation
"""
for f in self . _tmpfiles :
logging . debug ( " Removing " + f )
os . unlink ( f )
for vol in self . _tmpvols :
logging . debug ( " Removing volume ' %s ' " , vol . name ( ) )
vol . delete ( 0 )
self . _tmpvols = [ ]
self . _tmpfiles = [ ]
self . install_devices = [ ]
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 )
2013-09-26 21:04:28 +04:00
except :
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
2014-09-07 19:57:04 +04: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 ) :
2014-09-12 23:59:22 +04:00
if disk . device == VirtualDisk . DEVICE_DISK :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_HARDDISK
2014-09-12 23:59:22 +04:00
elif disk . device == VirtualDisk . DEVICE_CDROM :
2013-07-17 15:53:47 +04:00
return OSXML . BOOT_DEVICE_CDROM
2014-09-12 23:59:22 +04:00
elif disk . device == VirtualDisk . 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