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-06-07 17:32:51 -04:00
from . import progress
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 . devices import DeviceDisk
2019-06-09 18:48:22 -04:00
from . installerinject import perform_initrd_injections
2018-10-12 18:35:09 -04:00
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-22 16:23:38 +01:00
def __init__ ( self , os_variant , kernel_pairs , os_media ) :
2019-01-31 13:42:02 -05:00
self . os_variant = os_variant
self . kernel_pairs = kernel_pairs
2019-03-22 16:23:38 +01:00
self . os_media = os_media
2019-03-08 17:01:26 -05:00
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-06-07 13:58:19 -04:00
@staticmethod
2019-06-07 14:08:56 -04:00
def get_system_scratchdir ( guest ) :
2019-06-07 13:58:19 -04:00
"""
Return the tmpdir that ' s accessible by VMs on system libvirt URIs
"""
2019-06-07 21:40:47 -04:00
if guest . conn . is_xen ( ) :
2019-06-07 13:58:19 -04:00
return " /var/lib/xen "
2019-06-07 14:08:56 -04:00
return " /var/lib/libvirt/boot "
2019-06-07 13:58:19 -04:00
@staticmethod
def make_scratchdir ( guest ) :
"""
Determine the scratchdir for this URI , create it if necessary .
scratchdir is the directory that ' s accessible by VMs
"""
2019-06-07 17:50:58 -04:00
user_scratchdir = os . path . join (
guest . conn . get_app_cache_dir ( ) , " boot " )
2019-06-07 14:08:56 -04:00
system_scratchdir = InstallerTreeMedia . get_system_scratchdir ( guest )
2019-06-07 13:58:19 -04:00
2019-06-07 14:08:56 -04:00
# If we are a session URI, or we don't have access to the system
# scratchdir, make sure the session scratchdir exists and use that.
if ( guest . conn . is_session_uri ( ) or
not os . path . exists ( system_scratchdir ) or
not os . access ( system_scratchdir , os . W_OK ) ) :
if not os . path . exists ( user_scratchdir ) :
os . makedirs ( user_scratchdir , 0o751 )
return user_scratchdir
2019-06-07 13:58:19 -04:00
2019-06-07 14:08:56 -04:00
return system_scratchdir
2019-06-07 13:58:19 -04:00
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 ) ) :
2019-06-09 10:49:47 -04:00
self . location = os . path . abspath ( self . location )
2018-10-12 18:35:09 -04:00
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 ) :
2019-06-07 17:32:51 -04:00
meter = progress . ensure_meter ( meter )
2018-10-12 18:35:09 -04:00
if not self . _cached_fetcher :
2019-06-07 13:58:19 -04:00
scratchdir = InstallerTreeMedia . 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-22 16:23:38 +01:00
os_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-22 16:23:38 +01:00
os_media = store . get_os_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 ,
2019-03-22 16:23:38 +01:00
os_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 )
2019-06-07 14:08:56 -04:00
system_scratchdir = InstallerTreeMedia . get_system_scratchdir ( guest )
2018-10-12 18:35:09 -04:00
kernel , initrd , tmpvols = upload_kernel_initrd (
2019-06-07 14:08:56 -04:00
guest . conn , fetcher . scratchdir , system_scratchdir ,
2018-10-12 18:35:09 -04:00
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
2019-06-08 14:16:52 -04:00
def _prepare_unattended_data ( self , guest , cache ) :
location = self . location if self . _media_type == MEDIA_URL else None
script = unattended . prepare_install_script (
guest , self . _unattended_data , location , cache . os_media )
2019-06-08 15:20:16 -04:00
expected_filename = script . get_expected_filename ( )
2019-06-08 14:16:52 -04:00
scriptpath = unattended . generate_install_script ( guest , script )
unattended_cmdline = script . generate_cmdline ( )
logging . debug ( " Generated unattended cmdline: %s " , unattended_cmdline )
2019-06-08 15:20:16 -04:00
self . initrd_injections . append ( ( scriptpath , expected_filename ) )
2019-06-08 14:16:52 -04:00
self . _tmpfiles . append ( scriptpath )
return unattended_cmdline
2018-10-12 18:35:09 -04:00
def prepare ( self , guest , meter ) :
2019-03-07 13:52:53 +01:00
fetcher = self . _get_fetcher ( guest , meter )
cache = self . _get_cached_data ( guest , fetcher )
2019-06-08 14:16:52 -04:00
unattended_cmdline = None
2019-02-22 09:40:17 +01:00
if self . _unattended_data :
2019-06-08 14:16:52 -04:00
unattended_cmdline = self . _prepare_unattended_data ( guest , cache )
2019-02-22 09:40:17 +01:00
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()
2019-06-08 13:01:43 -04:00
if unattended_cmdline :
if self . location in a and self . location in unattended_cmdline :
# Latest libosinfo will handle the URL arg by itself,
# don't double it up
a = unattended_cmdline
else :
a = " %s %s " % ( unattended_cmdline , a )
2019-02-22 09:40:19 +01:00
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
2019-03-22 16:23:40 +01:00
def requires_internet ( self , guest , meter ) :
if self . _media_type in [ MEDIA_URL , MEDIA_DIR ] :
return True
fetcher = self . _get_fetcher ( guest , meter )
cache = self . _get_cached_data ( guest , fetcher )
if cache . os_media :
2019-06-09 11:26:28 -04:00
return cache . os_media . is_netinst ( )
2019-03-22 16:23:40 +01:00
return False