2018-10-12 18:35:09 -04:00
#
# Copyright 2006-2009, 2013, 2014 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import logging
import os
2019-03-05 13:59:56 -05:00
from . import unattended
2018-10-12 18:35:09 -04:00
from . import urldetect
from . import urlfetcher
from . import util
from . devices import DeviceDisk
from . initrdinject import perform_initrd_injections
from . kernelupload import upload_kernel_initrd
2019-01-31 17:11:39 -05:00
from . osdict import OSDB
2018-10-12 18:35:09 -04:00
# Enum of the various install media types we can have
( MEDIA_DIR ,
MEDIA_ISO ,
MEDIA_URL ) = range ( 1 , 4 )
def _is_url ( url ) :
return ( url . startswith ( " http:// " ) or
url . startswith ( " https:// " ) or
url . startswith ( " ftp:// " ) )
2019-01-31 13:42:02 -05:00
class _LocationData ( object ) :
2019-03-08 17:01:26 -05:00
def __init__ ( self , os_variant , kernel_pairs , osinfo_media ) :
2019-01-31 13:42:02 -05:00
self . os_variant = os_variant
self . kernel_pairs = kernel_pairs
2019-03-08 17:01:26 -05:00
self . osinfo_media = osinfo_media
2019-01-31 17:11:39 -05:00
self . kernel_url_arg = None
if self . os_variant :
osobj = OSDB . lookup_os ( self . os_variant )
self . kernel_url_arg = osobj . get_kernel_url_arg ( )
2019-01-31 13:42:02 -05:00
2018-10-12 18:35:09 -04:00
class InstallerTreeMedia ( object ) :
"""
Class representing - - location Tree media . Can be one of
- A network URL : http : / / dl . fedoraproject . org / . . .
- A local directory
- A local . iso file , which will be accessed with isoinfo
"""
@staticmethod
def validate_path ( conn , path ) :
try :
dev = DeviceDisk ( conn )
dev . device = dev . DEVICE_CDROM
dev . path = path
dev . validate ( )
return dev . path
except Exception as e :
logging . debug ( " Error validating install location " , exc_info = True )
if path . startswith ( " nfs: " ) :
logging . warning ( " NFS URL installs are no longer supported. "
" Access your install media over an alternate transport "
" like HTTP, or manually mount the NFS share and install "
" from the local directory mount point. " )
raise ValueError ( _ ( " Validating install media ' %s ' failed: %s " ) %
( str ( path ) , e ) )
2019-01-31 18:07:09 -05:00
def __init__ ( self , conn , location , location_kernel , location_initrd ) :
2018-10-12 18:35:09 -04:00
self . conn = conn
self . location = location
2019-01-31 18:07:09 -05:00
self . _location_kernel = location_kernel
self . _location_initrd = location_initrd
2018-10-12 18:35:09 -04:00
self . initrd_injections = [ ]
self . _cached_fetcher = None
2019-01-31 13:42:02 -05:00
self . _cached_data = None
2018-10-12 18:35:09 -04:00
self . _tmpfiles = [ ]
self . _tmpvols = [ ]
2019-02-22 09:40:17 +01:00
self . _unattended_data = None
2018-10-12 18:35:09 -04:00
self . _media_type = MEDIA_ISO
if ( not self . conn . is_remote ( ) and
os . path . exists ( self . location ) and
os . path . isdir ( self . location ) ) :
self . _media_type = MEDIA_DIR
elif _is_url ( self . location ) :
self . _media_type = MEDIA_URL
2019-01-30 17:07:36 -05:00
if self . conn . is_remote ( ) and not self . _media_type == MEDIA_URL :
raise ValueError ( _ ( " Cannot access install tree on remote "
" connection: %s " ) % self . location )
2018-10-12 18:35:09 -04:00
if self . _media_type == MEDIA_ISO :
InstallerTreeMedia . validate_path ( self . conn , self . location )
########################
# Install preparations #
########################
def _get_fetcher ( self , guest , meter ) :
meter = util . ensure_meter ( meter )
if not self . _cached_fetcher :
2019-02-07 13:58:55 -05:00
scratchdir = util . make_scratchdir ( guest )
2018-10-12 18:35:09 -04:00
self . _cached_fetcher = urlfetcher . fetcherForURI (
self . location , scratchdir , meter )
self . _cached_fetcher . meter = meter
return self . _cached_fetcher
2019-01-31 13:42:02 -05:00
def _get_cached_data ( self , guest , fetcher ) :
if not self . _cached_data :
2019-01-31 18:07:09 -05:00
has_location_kernel = bool (
self . _location_kernel and self . _location_initrd )
store = urldetect . getDistroStore ( guest , fetcher ,
skip_error = has_location_kernel )
os_variant = None
2019-03-08 17:01:26 -05:00
osinfo_media = None
2019-01-31 18:07:09 -05:00
kernel_paths = [ ]
if store :
kernel_paths = store . get_kernel_paths ( )
os_variant = store . get_osdict_info ( )
2019-03-08 17:01:26 -05:00
osinfo_media = store . get_osinfo_media ( )
2019-01-31 18:07:09 -05:00
if has_location_kernel :
kernel_paths = [
( self . _location_kernel , self . _location_initrd ) ]
2019-03-08 17:01:26 -05:00
self . _cached_data = _LocationData ( os_variant , kernel_paths ,
osinfo_media )
2019-01-31 13:42:02 -05:00
return self . _cached_data
2018-10-12 18:35:09 -04:00
def _prepare_kernel_url ( self , guest , fetcher ) :
2019-01-31 13:42:02 -05:00
cache = self . _get_cached_data ( guest , fetcher )
def _check_kernel_pairs ( ) :
for kpath , ipath in cache . kernel_pairs :
if fetcher . hasFile ( kpath ) and fetcher . hasFile ( ipath ) :
return kpath , ipath
raise RuntimeError ( _ ( " Couldn ' t find kernel for install tree. " ) )
2019-01-29 16:05:27 -05:00
2019-01-31 13:42:02 -05:00
kernelpath , initrdpath = _check_kernel_pairs ( )
2019-01-29 16:05:27 -05:00
kernel = fetcher . acquireFile ( kernelpath )
2018-10-12 18:35:09 -04:00
self . _tmpfiles . append ( kernel )
2019-01-29 16:05:27 -05:00
initrd = fetcher . acquireFile ( initrdpath )
self . _tmpfiles . append ( initrd )
args = " "
2019-01-31 13:42:02 -05:00
if not self . location . startswith ( " / " ) and cache . kernel_url_arg :
args + = " %s = %s " % ( cache . kernel_url_arg , self . location )
2018-10-12 18:35:09 -04:00
perform_initrd_injections ( initrd ,
self . initrd_injections ,
fetcher . scratchdir )
kernel , initrd , tmpvols = upload_kernel_initrd (
guest . conn , fetcher . scratchdir ,
util . get_system_scratchdir ( guest . type ) ,
fetcher . meter , kernel , initrd )
self . _tmpvols + = tmpvols
return kernel , initrd , args
##############
# Public API #
##############
2019-02-22 09:40:17 +01:00
def set_unattended_data ( self , unattended_data ) :
self . _unattended_data = unattended_data
2018-10-12 18:35:09 -04:00
def prepare ( self , guest , meter ) :
2019-02-22 09:40:19 +01:00
cmdline = None
2019-02-22 09:40:17 +01:00
if self . _unattended_data :
2019-03-07 13:52:50 +01:00
location = self . location if self . _media_type == MEDIA_URL else None
2019-03-05 14:27:37 -05:00
script = unattended . prepare_install_script (
2019-03-07 13:52:50 +01:00
guest , self . _unattended_data , location )
2019-03-05 14:27:37 -05:00
path , cmdline = unattended . generate_install_script ( script )
2019-02-22 09:40:19 +01:00
2019-03-05 16:26:36 -05:00
logging . debug ( " Generated unattended cmdline: %s " , cmdline )
logging . debug ( " Generated unattended script: %s " , path )
logging . debug ( " Generated script contents: \n %s " ,
open ( path ) . read ( ) )
2019-02-22 09:40:19 +01:00
self . initrd_injections . append ( path )
self . _tmpfiles . append ( path )
2019-02-22 09:40:17 +01:00
2018-10-12 18:35:09 -04:00
fetcher = self . _get_fetcher ( guest , meter )
2019-02-22 09:40:19 +01:00
k , i , a = self . _prepare_kernel_url ( guest , fetcher )
# If a cmdline was set due to unattended installation, prepend the
# unattended kernel cmdline to the args returned by
# _prepare_kernel_url()
if cmdline :
a = " %s %s " % ( cmdline , a )
return k , i , a
2018-10-12 18:35:09 -04:00
def cleanup ( self , guest ) :
ignore = guest
for f in self . _tmpfiles :
logging . debug ( " Removing %s " , str ( f ) )
os . unlink ( f )
for vol in self . _tmpvols :
logging . debug ( " Removing volume ' %s ' " , vol . name ( ) )
vol . delete ( 0 )
self . _tmpvols = [ ]
self . _tmpfiles = [ ]
def cdrom_path ( self ) :
if self . _media_type in [ MEDIA_ISO ] :
return self . location
def detect_distro ( self , guest ) :
2019-01-29 17:00:30 -05:00
fetcher = self . _get_fetcher ( guest , None )
2019-01-31 13:42:02 -05:00
cache = self . _get_cached_data ( guest , fetcher )
return cache . os_variant