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 os
from . import urldetect
from . import urlfetcher
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-06-16 21:34:47 -04:00
from . . import progress
from . . devices import DeviceDisk
from . . logger import log
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 ,
2019-06-12 17:25:56 -04:00
MEDIA_URL ,
MEDIA_KERNEL ) = range ( 1 , 5 )
2018-10-12 18:35:09 -04:00
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 :
2019-06-16 21:12:39 -04:00
log . debug ( " Error validating install location " , exc_info = True )
2018-10-12 18:35:09 -04:00
if path . startswith ( " nfs: " ) :
2019-06-16 21:12:39 -04:00
log . warning ( " NFS URL installs are no longer supported. "
2018-10-12 18:35:09 -04:00
" 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 ) :
2019-06-10 14:15:50 -04:00
os . makedirs ( user_scratchdir , 0o751 ) # pragma: no cover
2019-06-07 14:08:56 -04:00
return user_scratchdir
2019-06-07 13:58:19 -04:00
2019-06-10 14:15:50 -04:00
return system_scratchdir # pragma: no cover
2019-06-07 13:58:19 -04:00
2019-06-12 17:25:56 -04:00
def __init__ ( self , conn , location , location_kernel , location_initrd ,
2019-06-12 17:55:30 -04:00
install_kernel , install_initrd , install_kernel_args ) :
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
2019-06-12 17:25:56 -04:00
self . _install_kernel = install_kernel
self . _install_initrd = install_initrd
2019-06-12 17:55:30 -04:00
self . _install_kernel_args = install_kernel_args
self . _initrd_injections = [ ]
self . _extra_args = [ ]
2018-10-12 18:35:09 -04:00
2019-06-11 19:20:33 -04:00
if location_kernel or location_initrd :
if not location :
raise ValueError ( _ ( " location kernel/initrd may only "
" be specified with a location URL/path " ) )
if not ( location_kernel and location_initrd ) :
raise ValueError ( _ ( " location kernel/initrd must be "
" be specified as a pair " ) )
2018-10-12 18:35:09 -04:00
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-06-12 17:25:56 -04:00
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 ) ) :
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-06-12 17:25:56 -04:00
else :
self . _media_type = MEDIA_ISO
2018-10-12 18:35:09 -04:00
2019-06-12 17:25:56 -04:00
if ( self . conn . is_remote ( ) and
not self . _media_type == MEDIA_URL and
not self . _media_type == MEDIA_KERNEL ) :
2019-01-30 17:07:36 -05:00
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
2019-06-12 17:25:56 -04:00
if self . _media_type == MEDIA_KERNEL :
self . _cached_fetcher = urlfetcher . DirectFetcher (
None , scratchdir , meter )
else :
self . _cached_fetcher = urlfetcher . fetcherForURI (
self . location , scratchdir , meter )
2018-10-12 18:35:09 -04:00
self . _cached_fetcher . meter = meter
return self . _cached_fetcher
2019-01-31 13:42:02 -05:00
def _get_cached_data ( self , guest , fetcher ) :
2019-06-12 17:25:56 -04:00
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 :
2019-01-31 18:07:09 -05:00
store = urldetect . getDistroStore ( guest , fetcher ,
skip_error = has_location_kernel )
2019-06-12 17:25:56 -04:00
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 )
2019-01-31 13:42:02 -05:00
return self . _cached_data
2018-10-12 18:35:09 -04:00
2019-06-10 13:54:49 -04:00
def _prepare_kernel_url ( self , guest , cache , fetcher ) :
2019-01-31 13:42:02 -05:00
def _check_kernel_pairs ( ) :
for kpath , ipath in cache . kernel_pairs :
if fetcher . hasFile ( kpath ) and fetcher . hasFile ( ipath ) :
return kpath , ipath
2019-06-10 14:15:50 -04:00
raise RuntimeError ( # pragma: no cover
_ ( " 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 )
2018-10-12 18:35:09 -04:00
perform_initrd_injections ( initrd ,
2019-06-12 17:55:30 -04:00
self . _initrd_injections ,
2018-10-12 18:35:09 -04:00
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
2019-06-10 13:54:49 -04:00
return kernel , initrd
2018-10-12 18:35:09 -04:00
##############
# Public API #
##############
2019-06-13 21:50:38 -04:00
def _prepare_unattended_data ( self , script ) :
2019-06-12 17:55:30 -04:00
if not script :
return
2019-06-09 19:10:19 -04:00
expected_filename = script . get_expected_filename ( )
2019-06-13 21:50:38 -04:00
scriptpath = script . write ( )
2019-06-08 14:16:52 -04:00
self . _tmpfiles . append ( scriptpath )
2019-06-12 17:55:30 -04:00
self . _initrd_injections . append ( ( scriptpath , expected_filename ) )
def _prepare_kernel_args ( self , cache , unattended_script ) :
install_args = None
if unattended_script :
install_args = unattended_script . generate_cmdline ( )
2019-06-16 21:12:39 -04:00
log . debug ( " Generated unattended cmdline: %s " , install_args )
2019-06-12 17:55:30 -04:00
elif self . is_network_url ( ) and cache . kernel_url_arg :
install_args = " %s = %s " % ( cache . kernel_url_arg , self . location )
if install_args :
self . _extra_args . append ( install_args )
if self . _install_kernel_args :
return self . _install_kernel_args
return " " . join ( self . _extra_args )
2019-06-08 14:16:52 -04:00
2019-06-09 19:10:19 -04:00
def prepare ( self , guest , meter , unattended_script ) :
2019-03-07 13:52:53 +01:00
fetcher = self . _get_fetcher ( guest , meter )
2019-06-10 13:54:49 -04:00
cache = self . _get_cached_data ( guest , fetcher )
2019-03-07 13:52:53 +01:00
2019-06-13 21:50:38 -04:00
self . _prepare_unattended_data ( unattended_script )
2019-06-12 17:55:30 -04:00
kernel_args = self . _prepare_kernel_args ( cache , unattended_script )
2019-02-22 09:40:17 +01:00
2019-06-10 13:54:49 -04:00
kernel , initrd = self . _prepare_kernel_url ( guest , cache , fetcher )
2019-06-12 17:55:30 -04:00
return kernel , initrd , kernel_args
2018-10-12 18:35:09 -04:00
def cleanup ( self , guest ) :
ignore = guest
for f in self . _tmpfiles :
2019-06-16 21:12:39 -04:00
log . debug ( " Removing %s " , str ( f ) )
2018-10-12 18:35:09 -04:00
os . unlink ( f )
for vol in self . _tmpvols :
2019-06-16 21:12:39 -04:00
log . debug ( " Removing volume ' %s ' " , vol . name ( ) )
2018-10-12 18:35:09 -04:00
vol . delete ( 0 )
self . _tmpvols = [ ]
self . _tmpfiles = [ ]
2019-06-12 17:55:30 -04:00
def set_initrd_injections ( self , initrd_injections ) :
self . _initrd_injections = initrd_injections
def set_extra_args ( self , extra_args ) :
self . _extra_args = extra_args
2018-10-12 18:35:09 -04:00
def cdrom_path ( self ) :
if self . _media_type in [ MEDIA_ISO ] :
return self . location
2019-06-10 14:20:45 -04:00
def is_network_url ( self ) :
2019-06-09 19:10:19 -04:00
if self . _media_type in [ MEDIA_URL ] :
return self . location
2018-10-12 18:35:09 -04:00
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
2019-06-09 19:10:19 -04:00
def get_os_media ( self , guest , meter ) :
fetcher = self . _get_fetcher ( guest , meter )
cache = self . _get_cached_data ( guest , fetcher )
return cache . os_media
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
2019-06-09 19:10:19 -04:00
os_media = self . get_os_media ( guest , meter )
if os_media :
return os_media . is_netinst ( )
2019-03-22 16:23:40 +01:00
return False