2018-03-29 01:46:07 +03:00
#
# Copyright 2006-2007, 2013 Red Hat, Inc.
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-29 01:46:07 +03:00
# See the COPYING file in the top-level directory.
import configparser
2020-07-18 03:51:54 +03:00
import os
2018-03-29 01:46:07 +03:00
import re
2019-06-17 04:34:47 +03:00
from . . logger import log
from . . osdict import OSDB
2018-03-29 01:46:07 +03:00
###############################################
# Helpers for detecting distro from given URL #
###############################################
2018-03-30 00:29:21 +03:00
class _DistroCache ( object ) :
def __init__ ( self , fetcher ) :
self . _fetcher = fetcher
self . _filecache = { }
self . _treeinfo = None
self . treeinfo_family = None
self . treeinfo_version = None
2018-04-24 18:32:42 +03:00
self . treeinfo_name = None
2019-01-29 22:52:16 +03:00
self . treeinfo_matched = False
2018-03-30 00:29:21 +03:00
2018-04-03 00:44:38 +03:00
self . suse_content = None
2018-10-19 00:16:14 +03:00
self . checked_for_suse_content = False
2018-03-30 00:29:21 +03:00
self . debian_media_type = None
2020-08-26 13:44:55 +03:00
self . mageia_version = None
2018-03-30 00:29:21 +03:00
2019-01-30 21:14:20 +03:00
self . libosinfo_os_variant = None
self . libosinfo_mediaobj = None
2019-07-16 18:14:26 +03:00
self . libosinfo_treeobj = None
2019-01-30 21:14:20 +03:00
2018-03-30 00:29:21 +03:00
def acquire_file_content ( self , path ) :
if path not in self . _filecache :
try :
content = self . _fetcher . acquireFileContent ( path )
2021-04-07 18:45:00 +03:00
except ValueError as e :
2018-03-30 00:29:21 +03:00
content = None
2021-04-07 18:45:00 +03:00
log . debug ( " Failed to acquire file= %s : %s " , path , e )
2018-03-30 00:29:21 +03:00
self . _filecache [ path ] = content
return self . _filecache [ path ]
@property
def treeinfo ( self ) :
if self . _treeinfo :
return self . _treeinfo
2019-03-18 21:22:25 +03:00
# Vast majority of trees here use .treeinfo. However, trees via
# Red Hat satellite on akamai CDN will use treeinfo, because akamai
# doesn't do dotfiles apparently:
#
# https://bugzilla.redhat.com/show_bug.cgi?id=635065
#
# Anaconda is the canonical treeinfo consumer and they check for both
# locations, so we need to do the same
treeinfostr = ( self . acquire_file_content ( " .treeinfo " ) or
self . acquire_file_content ( " treeinfo " ) )
2018-03-30 00:29:21 +03:00
if treeinfostr is None :
return None
# If the file doesn't parse or there's no 'family', this will
# error, but that should be fine because we aren't going to
# successfully detect the tree anyways
2020-07-05 15:12:47 +03:00
treeinfo = configparser . ConfigParser ( )
2018-03-30 00:29:21 +03:00
treeinfo . read_string ( treeinfostr )
self . treeinfo_family = treeinfo . get ( " general " , " family " )
self . _treeinfo = treeinfo
2019-06-17 04:12:39 +03:00
log . debug ( " treeinfo family= %s " , self . treeinfo_family )
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
if self . _treeinfo . has_option ( " general " , " version " ) :
self . treeinfo_version = self . _treeinfo . get ( " general " , " version " )
2019-06-17 04:12:39 +03:00
log . debug ( " Found treeinfo version= %s " , self . treeinfo_version )
2018-03-29 01:46:07 +03:00
2018-04-24 18:32:42 +03:00
if self . _treeinfo . has_option ( " general " , " name " ) :
self . treeinfo_name = self . _treeinfo . get ( " general " , " name " )
2019-06-17 04:12:39 +03:00
log . debug ( " Found treeinfo name= %s " , self . treeinfo_name )
2018-04-24 18:32:42 +03:00
2018-03-30 00:29:21 +03:00
return self . _treeinfo
def treeinfo_family_regex ( self , famregex ) :
if not self . treeinfo :
return False
ret = bool ( re . match ( famregex , self . treeinfo_family ) )
2019-01-29 22:52:16 +03:00
self . treeinfo_matched = ret
2018-03-30 00:29:21 +03:00
if not ret :
2019-06-17 04:12:39 +03:00
log . debug ( " Didn ' t match treeinfo family regex= %s " , famregex )
2018-03-30 00:29:21 +03:00
return ret
def content_regex ( self , filename , regex ) :
"""
Fetch ' filename ' and return True / False if it matches the regex
"""
content = self . acquire_file_content ( filename )
if content is None :
return False
for line in content . splitlines ( ) :
if re . match ( regex , line ) :
return True
2019-06-17 04:12:39 +03:00
log . debug ( " found filename= %s but regex= %s didn ' t match " ,
2018-03-30 00:29:21 +03:00
filename , regex )
return False
2018-03-29 01:46:07 +03:00
2018-04-18 00:59:26 +03:00
def get_treeinfo_media ( self , typ ) :
"""
Pull kernel / initrd / boot . iso paths out of the treeinfo for
the passed data
"""
def _get_treeinfo_path ( media_name ) :
image_type = self . treeinfo . get ( " general " , " arch " )
if typ == " xen " :
image_type = " xen "
return self . treeinfo . get ( " images- %s " % image_type , media_name )
try :
2018-10-12 22:50:52 +03:00
return [ ( _get_treeinfo_path ( " kernel " ) ,
_get_treeinfo_path ( " initrd " ) ) ]
2019-06-16 21:24:31 +03:00
except Exception : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Failed to parse treeinfo kernel/initrd " ,
2018-04-18 00:59:26 +03:00
exc_info = True )
2018-10-12 22:50:52 +03:00
return [ ]
2018-04-18 00:59:26 +03:00
2018-05-08 00:39:35 +03:00
def split_version ( self ) :
verstr = self . treeinfo_version
def _safeint ( c ) :
try :
return int ( c )
except Exception :
return 0
# Parse a string like 6.9 or 7.4 into its two parts
# centos altarch's have just version=7
update = 0
version = _safeint ( verstr )
2019-01-31 00:30:22 +03:00
if verstr . count ( " . " ) > = 1 :
2018-05-08 00:39:35 +03:00
version = _safeint ( verstr . split ( " . " ) [ 0 ] )
update = _safeint ( verstr . split ( " . " ) [ 1 ] )
2019-06-17 04:12:39 +03:00
log . debug ( " converted verstr= %s to version= %s update= %s " ,
2018-05-08 00:39:35 +03:00
verstr , version , update )
return version , update
2018-10-09 16:52:23 +03:00
def fetcher_is_iso ( self ) :
return self . _fetcher . is_iso ( )
2019-01-30 21:14:20 +03:00
def guess_os_from_iso ( self ) :
ret = OSDB . guess_os_by_iso ( self . _fetcher . location )
if not ret :
return False
self . libosinfo_os_variant , self . libosinfo_mediaobj = ret
if ( not self . libosinfo_mediaobj . get_kernel_path ( ) or
2020-07-18 03:51:54 +03:00
not self . libosinfo_mediaobj . get_initrd_path ( ) ) : # pragma: no cover
2019-01-30 21:14:20 +03:00
# This can happen if the media is live media, or just
# with incomplete libosinfo data
2019-06-17 04:12:39 +03:00
log . debug ( " libosinfo didn ' t report any media kernel/initrd "
2019-01-30 21:14:20 +03:00
" path for detected os_variant= %s " ,
self . libosinfo_mediaobj )
return False
2019-02-04 02:14:33 +03:00
return True
2019-01-30 21:14:20 +03:00
2019-02-04 02:14:33 +03:00
def guess_os_from_tree ( self ) :
ret = OSDB . guess_os_by_tree ( self . _fetcher . location )
if not ret :
return False
2019-07-16 18:14:26 +03:00
self . libosinfo_os_variant , self . libosinfo_treeobj = ret
2019-02-04 02:14:33 +03:00
self . treeinfo_matched = True
2019-01-30 21:14:20 +03:00
return True
2018-03-29 01:46:07 +03:00
2018-04-03 00:44:38 +03:00
class _SUSEContent ( object ) :
"""
Helper class tracking the SUSE ' content ' files
"""
def __init__ ( self , content_str ) :
self . content_str = content_str
self . content_dict = { }
for line in self . content_str . splitlines ( ) :
for prefix in [ " LABEL " , " DISTRO " , " VERSION " ,
2018-04-04 23:46:29 +03:00
" BASEARCHS " , " DEFAULTBASE " , " REPOID " ] :
2018-04-03 00:44:38 +03:00
if line . startswith ( prefix + " " ) :
self . content_dict [ prefix ] = line . split ( " " , 1 ) [ 1 ]
2019-06-17 04:34:47 +03:00
log . debug ( " SUSE content dict: %s " , self . content_dict )
2018-04-03 00:44:38 +03:00
self . tree_arch = self . _get_tree_arch ( )
self . product_name = self . _get_product_name ( )
self . product_version = self . _get_product_version ( )
2019-06-17 04:12:39 +03:00
log . debug ( " SUSE content product_name= %s product_version= %s "
2018-04-03 00:44:38 +03:00
" tree_arch= %s " , self . product_name , self . product_version ,
self . tree_arch )
def _get_tree_arch ( self ) :
# Examples:
# opensuse 11.4: BASEARCHS i586 x86_64
# opensuse 12.3: BASEARCHS i586 x86_64
# opensuse 10.3: DEFAULTBASE i586
distro_arch = ( self . content_dict . get ( " BASEARCHS " ) or
self . content_dict . get ( " DEFAULTBASE " ) )
if not distro_arch and " REPOID " in self . content_dict :
distro_arch = self . content_dict [ " REPOID " ] . rsplit ( ' / ' , 1 ) [ 1 ]
2018-04-04 23:46:29 +03:00
if not distro_arch :
2020-07-18 03:51:54 +03:00
return None # pragma: no cover
2018-04-03 00:44:38 +03:00
2018-04-04 23:46:29 +03:00
tree_arch = distro_arch . strip ( )
# Fix for 13.2 official oss repo
if tree_arch . find ( " i586-x86_64 " ) != - 1 :
tree_arch = " x86_64 "
2018-04-03 00:44:38 +03:00
return tree_arch
def _get_product_name ( self ) :
"""
Parse the SUSE product name . Examples :
SUSE Linux Enterprise Server 11 SP4
openSUSE 11.4
"""
# Some field examples in the wild
#
# opensuse 10.3: LABEL openSUSE 10.3
# opensuse 11.4: LABEL openSUSE 11.4
# opensuse 12.3: LABEL openSUSE
# sles11sp4 DVD: LABEL SUSE Linux Enterprise Server 11 SP4
#
#
# DISTRO cpe:/o:opensuse:opensuse:13.2,openSUSE
# DISTRO cpe:/o:suse:sled:12:sp3,SUSE Linux Enterprise Desktop 12 SP3
#
# As of 2018 all latest distros match only DISTRO and REPOID.
product_name = None
if " LABEL " in self . content_dict :
product_name = self . content_dict [ " LABEL " ]
elif " , " in self . content_dict . get ( " DISTRO " , " " ) :
product_name = self . content_dict [ " DISTRO " ] . rsplit ( " , " , 1 ) [ 1 ]
2019-06-17 04:12:39 +03:00
log . debug ( " SUSE content product_name= %s " , product_name )
2018-04-03 00:44:38 +03:00
return product_name
def _get_product_version ( self ) :
# Some example fields:
#
# opensuse 10.3: VERSION 10.3
# opensuse 12.3: VERSION 12.3
# SLES-10-SP4-DVD-x86_64-GM-DVD1.iso: VERSION 10.4-0
#
# REPOID obsproduct://build.suse.de/SUSE:SLE-11-SP4:GA/SUSE_SLES/11.4/DVD/x86_64
# REPOID obsproduct://build.suse.de/SUSE:SLE-12-SP3:GA/SLES/12.3/DVD/aarch64
#
# As of 2018 all latest distros match only DISTRO and REPOID.
if not self . product_name :
2020-07-18 03:51:54 +03:00
return None # pragma: no cover
2018-04-03 00:44:38 +03:00
distro_version = self . content_dict . get ( " VERSION " , " " )
if " - " in distro_version :
distro_version = distro_version . split ( ' - ' , 1 ) [ 0 ]
# Special case, parse version out of a line like this
# cpe:/o:opensuse:opensuse:13.2,openSUSE
if ( not distro_version and
2018-05-08 00:39:37 +03:00
re . match ( " ^.*:.*,openSUSE* " , self . content_dict [ " DISTRO " ] ) ) :
2018-04-03 00:44:38 +03:00
distro_version = self . content_dict [ " DISTRO " ] . rsplit (
" , " , 1 ) [ 0 ] . strip ( ) . rsplit ( " : " ) [ 4 ]
2018-05-08 00:39:37 +03:00
distro_version = distro_version . strip ( )
2018-04-03 00:44:38 +03:00
if " Enterprise " in self . product_name or " SLES " in self . product_name :
sle_version = self . product_name . strip ( ) . rsplit ( ' ' ) [ 4 ]
if len ( self . product_name . strip ( ) . rsplit ( ' ' ) ) > 5 :
sle_version = ( sle_version + ' . ' +
self . product_name . strip ( ) . rsplit ( ' ' ) [ 5 ] [ 2 ] )
distro_version = sle_version
return distro_version
2018-03-29 01:46:07 +03:00
2019-02-01 02:07:09 +03:00
def getDistroStore ( guest , fetcher , skip_error ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Finding distro store for location= %s " , fetcher . location )
2018-03-29 01:46:07 +03:00
arch = guest . os . arch
_type = guest . os . os_type
2018-09-13 00:23:01 +03:00
osobj = guest . osinfo
2019-03-09 00:54:26 +03:00
stores = _build_distro_list ( osobj )
2018-03-30 00:29:21 +03:00
cache = _DistroCache ( fetcher )
2018-03-29 01:46:07 +03:00
for sclass in stores :
2018-03-30 00:29:21 +03:00
if not sclass . is_valid ( cache ) :
continue
2019-01-31 21:42:02 +03:00
store = sclass ( fetcher . location , arch , _type , cache )
2019-06-17 04:12:39 +03:00
log . debug ( " Detected class= %s osvariant= %s " ,
2018-04-18 00:26:55 +03:00
store . __class__ . __name__ , store . get_osdict_info ( ) )
2018-03-30 00:29:21 +03:00
return store
2018-03-29 01:46:07 +03:00
2019-02-01 02:07:09 +03:00
if skip_error :
return None
2018-03-29 01:46:07 +03:00
# No distro was detected. See if the URL even resolves, and if not
# give the user a hint that maybe they mistyped. This won't always
# be true since some webservers don't allow directory listing.
2018-04-30 15:56:53 +03:00
# https://www.redhat.com/archives/virt-tools-list/2014-December/msg00048.html
2018-03-29 01:46:07 +03:00
extramsg = " "
2018-03-30 02:22:28 +03:00
if not fetcher . can_access ( ) :
2018-03-29 01:46:07 +03:00
extramsg = ( " : " +
_ ( " The URL could not be accessed, maybe you mistyped? " ) )
2020-07-12 00:31:40 +03:00
msg = ( _ ( " Could not find an installable distribution at URL ' %s ' " ) %
fetcher . location )
msg + = extramsg
msg + = " \n \n "
msg + = _ ( " The location must be the root directory of an install tree. \n "
" See virt-install man page for various distro examples. " )
raise ValueError ( msg )
2018-03-29 01:46:07 +03:00
##################
# Distro classes #
##################
2019-01-30 19:56:48 +03:00
class _DistroTree ( object ) :
2018-03-29 01:46:07 +03:00
"""
2019-01-30 19:56:48 +03:00
Class for determining the kernel / initrd path for an install
tree ( URL , ISO , or local directory )
2018-03-29 01:46:07 +03:00
"""
2018-03-29 18:35:18 +03:00
PRETTY_NAME = None
2018-09-01 15:41:22 +03:00
matching_distros = [ ]
2018-03-29 01:46:07 +03:00
2019-01-31 21:42:02 +03:00
def __init__ ( self , location , arch , vmtype , cache ) :
2018-03-29 01:46:07 +03:00
self . type = vmtype
self . arch = arch
2019-01-31 21:42:02 +03:00
self . uri = location
2018-03-30 00:29:21 +03:00
self . cache = cache
2018-03-29 01:46:07 +03:00
2019-01-30 21:14:20 +03:00
if self . cache . libosinfo_os_variant :
self . _os_variant = self . cache . libosinfo_os_variant
else :
self . _os_variant = self . _detect_version ( )
2020-07-18 03:51:54 +03:00
if ( self . _os_variant and
not OSDB . lookup_os ( self . _os_variant ) ) :
log . debug ( " Detected os_variant as %s , which is not in osdict. " ,
self . _os_variant )
2018-04-18 00:26:55 +03:00
self . _os_variant = None
2019-01-29 21:54:43 +03:00
self . _kernel_paths = [ ]
2019-01-29 22:52:16 +03:00
if self . cache . treeinfo_matched :
self . _kernel_paths = self . cache . get_treeinfo_media ( self . type )
else :
2019-01-29 22:54:41 +03:00
self . _set_manual_kernel_paths ( )
2019-01-29 21:54:43 +03:00
2018-04-18 00:26:55 +03:00
2019-01-29 22:54:41 +03:00
def _set_manual_kernel_paths ( self ) :
"""
If kernel / initrd path could not be determined from a source
like treeinfo , subclasses can override this to set a list
of manual paths
"""
2019-01-29 21:54:43 +03:00
2019-01-30 00:05:27 +03:00
def _detect_version ( self ) :
"""
Hook for subclasses to detect media os variant .
"""
2019-06-17 04:12:39 +03:00
log . debug ( " %s does not implement any osdict detection " , self )
2019-01-30 00:05:27 +03:00
return None
2018-03-29 01:46:07 +03:00
2019-01-30 00:05:27 +03:00
##############
# Public API #
##############
2018-03-29 01:46:07 +03:00
2019-01-30 00:05:27 +03:00
@classmethod
def is_valid ( cls , cache ) :
raise NotImplementedError
2019-01-31 21:42:02 +03:00
def get_kernel_paths ( self ) :
return self . _kernel_paths
2018-03-29 01:46:07 +03:00
def get_osdict_info ( self ) :
"""
2018-04-18 00:26:55 +03:00
Return detected osdict value
2018-03-29 01:46:07 +03:00
"""
2018-04-18 00:26:55 +03:00
return self . _os_variant
2018-03-29 01:46:07 +03:00
2019-03-22 18:23:38 +03:00
def get_os_media ( self ) :
2019-03-09 01:01:26 +03:00
"""
2019-03-22 18:23:38 +03:00
Return an OsMedia wrapper around the detected libosinfo media object
2019-03-09 01:01:26 +03:00
"""
2019-06-09 18:26:28 +03:00
return self . cache . libosinfo_mediaobj
2019-03-09 01:01:26 +03:00
2019-07-16 18:14:27 +03:00
def get_os_tree ( self ) :
"""
Return an OsTree wrapper around the detected libosinfo media object
"""
return self . cache . libosinfo_treeobj
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _FedoraDistro ( _DistroTree ) :
2018-03-29 18:35:18 +03:00
PRETTY_NAME = " Fedora "
2018-09-01 15:41:22 +03:00
matching_distros = [ " fedora " ]
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
famregex = " .*Fedora.* "
return cache . treeinfo_family_regex ( famregex )
2018-04-18 00:45:57 +03:00
def _detect_version ( self ) :
2019-01-08 23:44:33 +03:00
latest_variant = " fedora-unknown "
2018-03-29 01:46:07 +03:00
2018-03-30 02:43:55 +03:00
verstr = self . cache . treeinfo_version
2019-06-16 21:24:31 +03:00
if not verstr : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " No treeinfo version? Assume latest_variant= %s " ,
2018-04-18 00:45:57 +03:00
latest_variant )
return latest_variant
2018-03-29 01:46:07 +03:00
2018-03-30 02:43:55 +03:00
# rawhide trees changed to use version=Rawhide in Apr 2016
if verstr in [ " development " , " rawhide " , " Rawhide " ] :
2019-06-17 04:12:39 +03:00
log . debug ( " treeinfo version= %s , using latest_variant= %s " ,
2018-04-18 00:45:57 +03:00
verstr , latest_variant )
return latest_variant
2018-03-29 01:46:07 +03:00
2018-03-30 02:43:55 +03:00
# treeinfo version is just an integer
2018-04-18 00:45:57 +03:00
variant = " fedora " + verstr
if OSDB . lookup_os ( variant ) :
return variant
2018-03-29 01:46:07 +03:00
2020-07-18 03:51:54 +03:00
log . debug (
2019-06-16 21:24:31 +03:00
" variant= %s from treeinfo version= %s not found, "
2018-04-18 00:45:57 +03:00
" using latest_variant= %s " , variant , verstr , latest_variant )
2020-07-18 03:51:54 +03:00
return latest_variant
2018-03-29 21:12:05 +03:00
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _RHELDistro ( _DistroTree ) :
2018-03-29 18:35:18 +03:00
PRETTY_NAME = " Red Hat Enterprise Linux "
2018-09-01 15:41:22 +03:00
matching_distros = [ " rhel " ]
2018-03-30 01:58:40 +03:00
_variant_prefix = " rhel "
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
# Matches:
# Red Hat Enterprise Linux
# RHEL Atomic Host
famregex = " .*(Red Hat Enterprise Linux|RHEL).* "
2020-07-18 03:51:54 +03:00
if cache . treeinfo_family_regex ( famregex ) :
return True
2018-03-30 00:29:21 +03:00
2018-03-29 21:12:05 +03:00
def _detect_version ( self ) :
2020-07-18 03:51:54 +03:00
if not self . cache . treeinfo_version : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " No treeinfo version? Not setting an os_variant " )
2018-03-29 02:37:19 +03:00
return
2018-05-08 00:39:35 +03:00
version , update = self . cache . split_version ( )
2018-03-30 01:58:40 +03:00
# start with example base=rhel7, then walk backwards
# through the OS list to find the latest os name that matches
# this way we handle rhel7.6 from treeinfo when osdict only
# knows about rhel7.5
base = self . _variant_prefix + str ( version )
while update > = 0 :
tryvar = base + " . %s " % update
2018-04-18 00:26:55 +03:00
if OSDB . lookup_os ( tryvar ) :
return tryvar
2018-03-30 01:58:40 +03:00
update - = 1
2018-03-29 02:37:19 +03:00
2019-01-30 19:56:48 +03:00
class _CentOSDistro ( _RHELDistro ) :
2018-03-29 18:35:18 +03:00
PRETTY_NAME = " CentOS "
2018-09-01 15:41:22 +03:00
matching_distros = [ " centos " ]
2018-03-30 01:58:40 +03:00
_variant_prefix = " centos "
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
2020-07-18 03:51:54 +03:00
if cache . treeinfo_family_regex ( " .*CentOS.* " ) :
return True
if cache . treeinfo_family_regex ( " .*Scientific.* " ) :
return True
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _SuseDistro ( _RHELDistro ) :
2018-05-08 00:39:38 +03:00
PRETTY_NAME = None
2018-04-03 01:42:04 +03:00
_suse_regex = [ ]
2018-09-01 15:41:22 +03:00
matching_distros = [ ]
2018-05-17 02:31:50 +03:00
_variant_prefix = NotImplementedError
famregex = NotImplementedError
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
2018-05-08 00:39:38 +03:00
if cache . treeinfo_family_regex ( cls . famregex ) :
2018-04-24 18:32:42 +03:00
return True
2018-10-19 00:16:14 +03:00
if not cache . checked_for_suse_content :
cache . checked_for_suse_content = True
2018-04-03 01:42:04 +03:00
content_str = cache . acquire_file_content ( " content " )
if content_str is None :
return False
try :
cache . suse_content = _SUSEContent ( content_str )
2020-07-18 03:51:54 +03:00
except Exception as e : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error parsing SUSE content file: %s " , str ( e ) )
2018-04-03 01:42:04 +03:00
return False
2018-10-19 00:16:14 +03:00
if not cache . suse_content :
2018-04-03 01:42:04 +03:00
return False
for regex in cls . _suse_regex :
2019-06-10 15:42:39 +03:00
if re . match ( regex , cache . suse_content . product_name or " " ) :
2018-04-03 01:42:04 +03:00
return True
return False
2018-03-29 01:46:07 +03:00
2019-01-29 22:54:41 +03:00
def _set_manual_kernel_paths ( self ) :
2019-01-29 22:52:16 +03:00
# We only reach here if no treeinfo was matched
2018-04-24 18:27:53 +03:00
tree_arch = self . cache . suse_content . tree_arch
2018-03-30 00:29:21 +03:00
2018-04-24 18:27:53 +03:00
if re . match ( r ' i[4-9]86 ' , tree_arch ) :
tree_arch = ' i386 '
2018-03-29 01:46:07 +03:00
oldkern = " linux "
oldinit = " initrd "
2018-04-24 18:27:53 +03:00
if tree_arch == " x86_64 " :
2018-03-29 01:46:07 +03:00
oldkern + = " 64 "
oldinit + = " 64 "
2018-03-29 18:49:34 +03:00
if self . type == " xen " :
2018-03-29 01:46:07 +03:00
# Matches Opensuse > 10.2 and sles 10
2018-03-29 18:49:34 +03:00
self . _kernel_paths . append (
2018-04-24 18:27:53 +03:00
( " boot/ %s /vmlinuz-xen " % tree_arch ,
" boot/ %s /initrd-xen " % tree_arch ) )
2018-03-29 18:49:34 +03:00
2019-06-16 20:50:36 +03:00
if str ( self . _os_variant ) . startswith ( ( " sles11 " , " sled11 " ) ) :
if tree_arch == " s390x " :
self . _kernel_paths . append (
( " boot/s390x/vmrdr.ikr " , " boot/s390x/initrd " ) )
if tree_arch == " ppc64 " :
self . _kernel_paths . append (
( " suseboot/linux64 " , " suseboot/initrd64 " ) )
2018-03-29 21:12:05 +03:00
2018-03-29 18:49:34 +03:00
# Tested with SLES 12 for ppc64le, all s390x
self . _kernel_paths . append (
2018-04-24 18:27:53 +03:00
( " boot/ %s /linux " % tree_arch ,
" boot/ %s /initrd " % tree_arch ) )
2018-03-29 18:49:34 +03:00
# Tested with Opensuse 10.0
self . _kernel_paths . append (
( " boot/loader/ %s " % oldkern ,
" boot/loader/ %s " % oldinit ) )
# Tested with Opensuse >= 10.2, 11, and sles 10
self . _kernel_paths . append (
2018-04-24 18:27:53 +03:00
( " boot/ %s /loader/linux " % tree_arch ,
" boot/ %s /loader/initrd " % tree_arch ) )
2018-03-29 18:49:34 +03:00
2018-04-18 00:07:19 +03:00
def _detect_osdict_from_suse_content ( self ) :
2018-04-24 18:32:42 +03:00
if not self . cache . suse_content :
2020-07-18 03:51:54 +03:00
return # pragma: no cover
2018-04-24 18:32:42 +03:00
2018-04-03 00:44:38 +03:00
distro_version = self . cache . suse_content . product_version
if not distro_version :
2020-07-18 03:51:54 +03:00
return # pragma: no cover
2018-04-03 00:44:38 +03:00
2018-03-29 01:46:07 +03:00
version = distro_version . split ( ' . ' , 1 ) [ 0 ] . strip ( )
2018-04-18 00:07:19 +03:00
2018-09-01 15:41:22 +03:00
if str ( self . _variant_prefix ) . startswith ( ( " sles " , " sled " ) ) :
2018-04-18 00:07:19 +03:00
sp_version = " "
if len ( distro_version . split ( ' . ' , 1 ) ) == 2 :
sp_version = ' sp ' + distro_version . split ( ' . ' , 1 ) [ 1 ] . strip ( )
2018-09-01 15:41:22 +03:00
return self . _variant_prefix + version + sp_version
2018-04-18 00:07:19 +03:00
2018-09-01 15:41:22 +03:00
return self . _variant_prefix + distro_version
2018-03-29 01:46:07 +03:00
def _detect_osdict_from_url ( self ) :
root = " opensuse "
oses = [ n for n in OSDB . list_os ( ) if n . name . startswith ( root ) ]
for osobj in oses :
codename = osobj . name [ len ( root ) : ]
if re . search ( " / %s / " % codename , self . uri ) :
return osobj . name
2018-04-18 00:07:19 +03:00
2018-04-24 18:32:42 +03:00
def _detect_from_treeinfo ( self ) :
if not self . cache . treeinfo_name :
return
if re . search ( " openSUSE Tumbleweed " , self . cache . treeinfo_name ) :
return " opensusetumbleweed "
2018-05-08 00:39:36 +03:00
version , update = self . cache . split_version ( )
base = self . _variant_prefix + str ( version )
while update > = 0 :
tryvar = base
2018-09-01 15:41:22 +03:00
# SLE doesn't use '.0' for initial releases in
# osinfo-db (sles11, sles12, etc)
2018-05-08 00:39:36 +03:00
if update > 0 or not base . startswith ( ' sle ' ) :
tryvar + = " . %s " % update
if OSDB . lookup_os ( tryvar ) :
return tryvar
update - = 1
2018-04-18 00:07:19 +03:00
def _detect_version ( self ) :
2018-04-24 18:32:42 +03:00
var = self . _detect_from_treeinfo ( )
if not var :
var = self . _detect_osdict_from_url ( )
2018-04-18 00:07:19 +03:00
if not var :
var = self . _detect_osdict_from_suse_content ( )
return var
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _SLESDistro ( _SuseDistro ) :
2018-05-08 00:39:38 +03:00
PRETTY_NAME = " SLES "
2018-09-01 15:41:22 +03:00
matching_distros = [ " sles " ]
2018-05-08 00:39:38 +03:00
_variant_prefix = " sles "
2018-04-03 01:42:04 +03:00
_suse_regex = [ " .*SUSE Linux Enterprise Server* " , " .*SUSE SLES* " ]
2018-05-08 00:39:38 +03:00
famregex = " .*SUSE Linux Enterprise.* "
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _SLEDDistro ( _SuseDistro ) :
2018-05-08 00:39:38 +03:00
PRETTY_NAME = " SLED "
2018-09-01 15:41:22 +03:00
matching_distros = [ " sled " ]
2018-05-08 00:39:38 +03:00
_variant_prefix = " sled "
2018-04-03 01:42:04 +03:00
_suse_regex = [ " .*SUSE Linux Enterprise Desktop* " ]
2018-05-08 00:39:38 +03:00
famregex = " .*SUSE Linux Enterprise.* "
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _OpensuseDistro ( _SuseDistro ) :
2018-05-08 00:39:38 +03:00
PRETTY_NAME = " openSUSE "
2018-09-01 15:41:22 +03:00
matching_distros = [ " opensuse " ]
2018-05-08 00:39:38 +03:00
_variant_prefix = " opensuse "
2018-04-03 01:42:04 +03:00
_suse_regex = [ " .*openSUSE.* " ]
2018-05-08 00:39:38 +03:00
famregex = " .*openSUSE.* "
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _DebianDistro ( _DistroTree ) :
2018-03-29 01:46:07 +03:00
# ex. http://ftp.egr.msu.edu/debian/dists/sarge/main/installer-i386/
2018-04-30 15:56:53 +03:00
# daily builds: https://d-i.debian.org/daily-images/amd64/
2018-03-29 18:35:18 +03:00
PRETTY_NAME = " Debian "
2018-09-01 15:41:22 +03:00
matching_distros = [ " debian " ]
2018-03-29 18:35:18 +03:00
_debname = " debian "
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
def check_manifest ( mfile ) :
is_ubuntu = cls . _debname == " ubuntu "
if cache . content_regex ( mfile , " .*[Uu]buntu.* " ) :
return is_ubuntu
return cache . content_regex ( mfile , " .*[Dd]ebian.* " )
media_type = None
if check_manifest ( " current/images/MANIFEST " ) :
media_type = " url "
2020-05-21 18:41:18 +03:00
elif check_manifest ( " current/legacy-images/MANIFEST " ) :
media_type = " legacy_url "
2018-03-30 00:29:21 +03:00
elif check_manifest ( " daily/MANIFEST " ) :
media_type = " daily "
elif cache . content_regex ( " .disk/info " ,
" %s .* " % cls . _debname . capitalize ( ) ) :
2018-10-09 16:52:23 +03:00
# There's two cases here:
# 1) Direct access ISO, attached as CDROM afterwards. We
# use one set of kernels in that case which seem to
# assume the prescence of CDROM media
# 2) ISO mounted and exported over URL. We use a different
# set of kernels that expect to boot from the network
if cache . fetcher_is_iso ( ) :
media_type = " disk "
else :
media_type = " mounted_iso_url "
2018-03-30 00:29:21 +03:00
if media_type :
cache . debian_media_type = media_type
return bool ( media_type )
2019-01-29 22:54:41 +03:00
def _set_manual_kernel_paths ( self ) :
2018-04-18 00:26:55 +03:00
if self . cache . debian_media_type == " disk " :
2018-03-29 21:12:05 +03:00
self . _set_installcd_paths ( )
2018-04-18 00:26:55 +03:00
else :
self . _set_url_paths ( )
2018-03-29 21:12:05 +03:00
2018-03-29 01:46:07 +03:00
def _find_treearch ( self ) :
2018-09-29 20:59:19 +03:00
for pattern in [ r " ^.*/installer-( \ w+)/?$ " ,
r " ^.*/daily-images/( \ w+)/?$ " ] :
2018-03-29 01:46:07 +03:00
arch = re . findall ( pattern , self . uri )
if not arch :
continue
2019-06-17 04:12:39 +03:00
log . debug ( " Found pattern= %s treearch= %s in uri " ,
2018-03-29 01:46:07 +03:00
pattern , arch [ 0 ] )
return arch [ 0 ]
2018-03-29 21:12:05 +03:00
# Check for standard arch strings which will be
2018-03-29 01:46:07 +03:00
# in the URI name for --location $ISO mounts
for arch in [ " i386 " , " amd64 " , " x86_64 " , " arm64 " ] :
if arch in self . uri :
2019-06-17 04:12:39 +03:00
log . debug ( " Found treearch= %s in uri " , arch )
2018-03-29 01:46:07 +03:00
if arch == " x86_64 " :
2020-07-18 03:51:54 +03:00
arch = " amd64 " # pragma: no cover
2018-03-29 01:46:07 +03:00
return arch
# Otherwise default to i386
arch = " i386 "
2019-06-17 04:12:39 +03:00
log . debug ( " No treearch found in uri, defaulting to arch= %s " , arch )
2018-03-29 01:46:07 +03:00
return arch
2018-04-18 00:26:55 +03:00
def _set_url_paths ( self ) :
url_prefix = " current/images "
if self . cache . debian_media_type == " daily " :
url_prefix = " daily "
2018-10-09 16:52:23 +03:00
elif self . cache . debian_media_type == " mounted_iso_url " :
url_prefix = " install "
2020-05-21 18:41:18 +03:00
elif self . cache . debian_media_type == " legacy_url " :
url_prefix = " current/legacy-images "
2018-04-18 00:26:55 +03:00
2018-03-29 21:12:05 +03:00
tree_arch = self . _find_treearch ( )
2018-03-30 00:29:21 +03:00
hvmroot = " %s /netboot/ %s -installer/ %s / " % ( url_prefix ,
self . _debname , tree_arch )
2018-03-29 01:46:07 +03:00
initrd_basename = " initrd.gz "
kernel_basename = " linux "
2018-03-29 21:12:05 +03:00
if tree_arch in [ " ppc64el " ] :
2018-03-29 01:46:07 +03:00
kernel_basename = " vmlinux "
2018-03-29 21:12:05 +03:00
if tree_arch == " s390x " :
hvmroot = " %s /generic/ " % url_prefix
2018-03-30 00:29:21 +03:00
kernel_basename = " kernel. %s " % self . _debname
initrd_basename = " initrd. %s " % self . _debname
2018-03-29 01:46:07 +03:00
2018-03-29 18:49:34 +03:00
if self . type == " xen " :
2018-03-29 21:12:05 +03:00
xenroot = " %s /netboot/xen/ " % url_prefix
2018-03-29 18:49:34 +03:00
self . _kernel_paths . append (
( xenroot + " vmlinuz " , xenroot + " initrd.gz " ) )
self . _kernel_paths . append (
( hvmroot + kernel_basename , hvmroot + initrd_basename ) )
2018-03-29 21:12:05 +03:00
def _set_installcd_paths ( self ) :
if self . _debname == " ubuntu " :
if not self . arch == " s390x " :
kpair = ( " install/vmlinuz " , " install/initrd.gz " )
else :
kpair = ( " boot/kernel.ubuntu " , " boot/initrd.ubuntu " )
elif self . arch == " x86_64 " :
kpair = ( " install.amd/vmlinuz " , " install.amd/initrd.gz " )
2018-03-29 01:46:07 +03:00
elif self . arch == " i686 " :
2018-03-29 21:12:05 +03:00
kpair = ( " install.386/vmlinuz " , " install.386/initrd.gz " )
2018-03-29 01:46:07 +03:00
elif self . arch == " aarch64 " :
2018-03-29 21:12:05 +03:00
kpair = ( " install.a64/vmlinuz " , " install.a64/initrd.gz " )
2018-03-29 01:46:07 +03:00
elif self . arch == " ppc64le " :
2018-03-29 21:12:05 +03:00
kpair = ( " install/vmlinux " , " install/initrd.gz " )
2018-03-29 01:46:07 +03:00
elif self . arch == " s390x " :
2018-03-29 21:12:05 +03:00
kpair = ( " boot/linux_vm " , " boot/root.bin " )
2018-03-29 01:46:07 +03:00
else :
2018-03-29 21:12:05 +03:00
kpair = ( " install/vmlinuz " , " install/initrd.gz " )
self . _kernel_paths + = [ kpair ]
2018-03-29 01:46:07 +03:00
return True
2018-04-18 00:26:55 +03:00
def _detect_version ( self ) :
2018-03-29 18:35:18 +03:00
oses = [ n for n in OSDB . list_os ( ) if n . name . startswith ( self . _debname ) ]
2018-03-29 01:46:07 +03:00
2018-04-18 00:26:55 +03:00
if self . cache . debian_media_type == " daily " :
2019-06-17 04:12:39 +03:00
log . debug ( " Appears to be debian ' daily ' URL, using latest "
2018-03-29 01:46:07 +03:00
" debian OS " )
return oses [ 0 ] . name
for osobj in oses :
if osobj . codename :
# Ubuntu codenames look like 'Warty Warthog'
codename = osobj . codename . split ( ) [ 0 ] . lower ( )
else :
if " " not in osobj . label :
2020-07-18 03:51:54 +03:00
continue # pragma: no cover
2018-03-29 01:46:07 +03:00
# Debian labels look like 'Debian Sarge'
codename = osobj . label . split ( ) [ 1 ] . lower ( )
if ( " / %s / " % codename ) in self . uri :
2019-06-17 04:12:39 +03:00
log . debug ( " Found codename= %s in the URL string " , codename )
2018-03-29 01:46:07 +03:00
return osobj . name
2019-01-30 19:56:48 +03:00
class _UbuntuDistro ( _DebianDistro ) :
2018-04-30 15:56:53 +03:00
# https://archive.ubuntu.com/ubuntu/dists/natty/main/installer-amd64/
2018-03-29 21:12:05 +03:00
PRETTY_NAME = " Ubuntu "
2018-09-01 15:41:22 +03:00
matching_distros = [ " ubuntu " ]
2018-03-29 21:12:05 +03:00
_debname = " ubuntu "
2018-03-29 01:46:07 +03:00
2019-06-10 20:36:07 +03:00
class _MageiaDistro ( _DistroTree ) :
# https://distro.ibiblio.org/mageia/distrib/cauldron/x86_64/
PRETTY_NAME = " Mageia "
matching_distros = [ " mageia " ]
2018-03-29 01:46:07 +03:00
2018-03-30 00:29:21 +03:00
@classmethod
def is_valid ( cls , cache ) :
2020-08-26 13:44:55 +03:00
if not cache . mageia_version :
content = cache . acquire_file_content ( " VERSION " )
if not content :
return False
2020-08-26 13:44:56 +03:00
m = re . match ( r " ^Mageia ( \ d+) .* " , content )
2020-08-26 13:44:55 +03:00
if not m :
2020-08-26 13:44:56 +03:00
return False # pragma: no cover
2020-08-26 13:44:55 +03:00
cache . mageia_version = m . group ( 1 )
return bool ( cache . mageia_version )
2018-03-30 00:29:21 +03:00
2019-01-29 22:54:41 +03:00
def _set_manual_kernel_paths ( self ) :
2018-03-29 18:49:34 +03:00
self . _kernel_paths + = [
2018-03-29 01:46:07 +03:00
( " isolinux/ %s /vmlinuz " % self . arch ,
" isolinux/ %s /all.rdz " % self . arch ) ]
2020-08-26 13:44:55 +03:00
def _detect_version ( self ) :
# version is just an integer
variant = " mageia " + self . cache . mageia_version
if OSDB . lookup_os ( variant ) :
return variant
2018-03-29 01:46:07 +03:00
2019-01-30 19:56:48 +03:00
class _GenericTreeinfoDistro ( _DistroTree ) :
2018-04-18 00:59:26 +03:00
"""
Generic catchall class for . treeinfo using distros
"""
PRETTY_NAME = " Generic Treeinfo "
2018-09-01 15:41:22 +03:00
matching_distros = [ ]
2018-04-18 00:59:26 +03:00
@classmethod
def is_valid ( cls , cache ) :
2019-01-29 22:52:16 +03:00
if cache . treeinfo :
cache . treeinfo_matched = True
return True
return False
2018-04-18 00:59:26 +03:00
2019-01-30 21:14:20 +03:00
class _LibosinfoDistro ( _DistroTree ) :
"""
For ISO media detection that was fully handled by libosinfo
"""
PRETTY_NAME = " Libosinfo detected "
matching_distros = [ ]
@classmethod
def is_valid ( cls , cache ) :
if cache . fetcher_is_iso ( ) :
return cache . guess_os_from_iso ( )
2019-02-04 02:14:33 +03:00
return cache . guess_os_from_tree ( )
2019-01-30 21:14:20 +03:00
def _set_manual_kernel_paths ( self ) :
self . _kernel_paths + = [
( self . cache . libosinfo_mediaobj . get_kernel_path ( ) ,
self . cache . libosinfo_mediaobj . get_initrd_path ( ) )
]
2019-03-09 00:54:26 +03:00
def _build_distro_list ( osobj ) :
2019-06-16 21:09:28 +03:00
allstores = [
# Libosinfo takes priority
_LibosinfoDistro ,
_FedoraDistro ,
_RHELDistro ,
_CentOSDistro ,
_SLESDistro ,
_SLEDDistro ,
_OpensuseDistro ,
_DebianDistro ,
_UbuntuDistro ,
_MageiaDistro ,
# Always stick GenericDistro at the end, since it's a catchall
_GenericTreeinfoDistro ,
]
2018-03-29 01:46:07 +03:00
2019-03-09 00:54:26 +03:00
# If user manually specified an os_distro, bump its URL class
# to the top of the list
if osobj . distro :
2019-06-17 04:12:39 +03:00
log . debug ( " variant= %s has distro= %s , looking for matching "
2019-03-09 00:54:26 +03:00
" distro store to prioritize " ,
osobj . name , osobj . distro )
found_store = None
for store in allstores :
if osobj . distro in store . matching_distros :
found_store = store
if found_store :
2019-06-17 04:12:39 +03:00
log . debug ( " Prioritizing distro store= %s " , found_store )
2019-03-09 00:54:26 +03:00
allstores . remove ( found_store )
allstores . insert ( 0 , found_store )
else :
2019-06-17 04:12:39 +03:00
log . debug ( " No matching store found, not prioritizing anything " )
2019-03-09 00:54:26 +03:00
2019-03-24 18:22:50 +03:00
force_libosinfo = os . environ . get ( " VIRTINST_TEST_SUITE_FORCE_LIBOSINFO " )
2020-07-18 03:51:54 +03:00
if force_libosinfo : # pragma: no cover
2019-03-24 18:22:50 +03:00
if bool ( int ( force_libosinfo ) ) :
allstores = [ _LibosinfoDistro ]
else :
allstores . remove ( _LibosinfoDistro )
2018-03-29 01:46:07 +03:00
return allstores