2013-03-18 01:06:52 +04:00
#
# List of OS Specific data
#
2014-09-09 15:37:20 +04:00
# Copyright 2006-2008, 2013-2014 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Jeremy Katz <katzj@redhat.com>
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
2015-04-14 00:05:25 +03:00
import logging
2014-09-23 01:20:07 +04:00
import re
2018-05-01 14:51:23 +03:00
import time
2014-09-23 01:20:07 +04:00
2015-09-04 00:31:43 +03:00
import gi
gi . require_version ( ' Libosinfo ' , ' 1.0 ' )
2014-09-23 01:20:07 +04:00
from gi . repository import Libosinfo as libosinfo
2018-05-01 14:51:23 +03:00
from gi . repository import GLib
2014-09-23 01:20:07 +04:00
2013-08-11 02:48:43 +04:00
2015-04-04 19:04:11 +03:00
###################
# Sorting helpers #
###################
2014-02-17 19:40:01 +04:00
2018-05-01 14:51:23 +03:00
def _sort ( tosort ) :
2013-03-18 01:06:52 +04:00
sortby_mappings = { }
distro_mappings = { }
retlist = [ ]
for key , osinfo in tosort . items ( ) :
2015-04-04 21:13:13 +03:00
# Libosinfo has some duplicate version numbers here, so append .1
# if there's a collision
sortby = osinfo . sortby
2014-02-17 19:40:00 +04:00
while sortby_mappings . get ( sortby ) :
sortby = sortby + " .1 "
2013-03-18 01:06:52 +04:00
sortby_mappings [ sortby ] = key
2018-09-01 15:41:22 +03:00
# Group by distro first, so debian is clumped together, fedora, etc.
distro = osinfo . distro
2013-03-18 01:06:52 +04:00
if distro not in distro_mappings :
distro_mappings [ distro ] = [ ]
distro_mappings [ distro ] . append ( sortby )
# We want returned lists to be sorted descending by 'distro', so we get
# debian5, debian4, fedora14, fedora13
# rather than
# debian4, debian5, fedora13, fedora14
2017-10-11 14:35:46 +03:00
for distro_list in list ( distro_mappings . values ( ) ) :
2013-03-18 01:06:52 +04:00
distro_list . sort ( )
distro_list . reverse ( )
2017-10-11 14:35:46 +03:00
sorted_distro_list = list ( distro_mappings . keys ( ) )
2013-03-18 01:06:52 +04:00
sorted_distro_list . sort ( )
2015-04-04 21:13:13 +03:00
# Build the final list of sorted os objects
2013-03-18 01:06:52 +04:00
for distro in sorted_distro_list :
distro_list = distro_mappings [ distro ]
for key in distro_list :
orig_key = sortby_mappings [ key ]
2013-08-11 22:52:30 +04:00
retlist . append ( tosort [ orig_key ] )
2013-03-18 01:06:52 +04:00
return retlist
2013-04-13 22:34:52 +04:00
2015-04-04 19:04:11 +03:00
class _OSDB ( object ) :
"""
Entry point for the public API
"""
def __init__ ( self ) :
self . __os_loader = None
self . __all_variants = None
# This is only for back compatibility with pre-libosinfo support.
# This should never change.
_aliases = {
2017-08-05 09:39:32 +03:00
" altlinux " : " altlinux1.0 " ,
" debianetch " : " debian4 " ,
" debianlenny " : " debian5 " ,
" debiansqueeze " : " debian6 " ,
" debianwheezy " : " debian7 " ,
" freebsd10 " : " freebsd10.0 " ,
" freebsd6 " : " freebsd6.0 " ,
" freebsd7 " : " freebsd7.0 " ,
" freebsd8 " : " freebsd8.0 " ,
" freebsd9 " : " freebsd9.0 " ,
" mandriva2009 " : " mandriva2009.0 " ,
" mandriva2010 " : " mandriva2010.0 " ,
" mbs1 " : " mbs1.0 " ,
" msdos " : " msdos6.22 " ,
" openbsd4 " : " openbsd4.2 " ,
" opensolaris " : " opensolaris2009.06 " ,
" opensuse11 " : " opensuse11.4 " ,
" opensuse12 " : " opensuse12.3 " ,
" rhel4 " : " rhel4.0 " ,
" rhel5 " : " rhel5.0 " ,
" rhel6 " : " rhel6.0 " ,
" rhel7 " : " rhel7.0 " ,
" ubuntuhardy " : " ubuntu8.04 " ,
" ubuntuintrepid " : " ubuntu8.10 " ,
" ubuntujaunty " : " ubuntu9.04 " ,
" ubuntukarmic " : " ubuntu9.10 " ,
" ubuntulucid " : " ubuntu10.04 " ,
" ubuntumaverick " : " ubuntu10.10 " ,
" ubuntunatty " : " ubuntu11.04 " ,
" ubuntuoneiric " : " ubuntu11.10 " ,
" ubuntuprecise " : " ubuntu12.04 " ,
" ubuntuquantal " : " ubuntu12.10 " ,
" ubunturaring " : " ubuntu13.04 " ,
" ubuntusaucy " : " ubuntu13.10 " ,
2015-04-04 19:37:46 +03:00
" virtio26 " : " fedora10 " ,
2017-08-05 09:39:32 +03:00
" vista " : " winvista " ,
" winxp64 " : " winxp " ,
2015-04-04 19:04:11 +03:00
2015-04-04 19:37:46 +03:00
# Old --os-type values
2017-08-05 09:39:32 +03:00
" linux " : " generic " ,
" windows " : " winxp " ,
" solaris " : " solaris10 " ,
2015-08-10 20:01:04 +03:00
" unix " : " freebsd9.0 " ,
2015-04-04 19:37:46 +03:00
" other " : " generic " ,
2015-04-04 19:04:11 +03:00
}
#################
# Internal APIs #
#################
def _make_default_variants ( self ) :
ret = { }
2013-08-10 22:44:20 +04:00
2015-04-04 19:04:11 +03:00
# Generic variant
v = _OsVariant ( None )
ret [ v . name ] = v
return ret
2013-08-10 02:14:42 +04:00
2015-04-04 19:04:11 +03:00
@property
def _os_loader ( self ) :
if not self . __os_loader :
loader = libosinfo . Loader ( )
loader . process_default_path ( )
self . __os_loader = loader
return self . __os_loader
@property
def _all_variants ( self ) :
if not self . __all_variants :
loader = self . _os_loader
allvariants = self . _make_default_variants ( )
db = loader . get_db ( )
oslist = db . get_os_list ( )
for os in range ( oslist . get_length ( ) ) :
osi = _OsVariant ( oslist . get_nth ( os ) )
allvariants [ osi . name ] = osi
2013-08-11 02:48:43 +04:00
2015-04-04 19:04:11 +03:00
self . __all_variants = allvariants
return self . __all_variants
###############
# Public APIs #
###############
2018-09-13 22:03:36 +03:00
def lookup_os_by_full_id ( self , full_id ) :
for osobj in self . _all_variants . values ( ) :
if osobj . full_id == full_id :
return osobj
2015-04-04 19:04:11 +03:00
def lookup_os ( self , key ) :
key = self . _aliases . get ( key ) or key
2015-08-10 20:01:04 +03:00
return self . _all_variants . get ( key )
2015-04-04 19:04:11 +03:00
def lookup_os_by_media ( self , location ) :
media = libosinfo . Media . create_from_location ( location , None )
ret = self . _os_loader . get_db ( ) . guess_os_from_media ( media )
2015-04-14 00:05:25 +03:00
if not ( ret and len ( ret ) > 0 and ret [ 0 ] ) :
return None
osname = ret [ 0 ] . get_short_id ( )
if osname == " fedora-unknown " :
osname = self . latest_fedora_version ( )
logging . debug ( " Detected location= %s as os=fedora-unknown. "
" Converting that to the latest fedora OS version= %s " ,
location , osname )
return osname
2015-04-04 19:04:11 +03:00
2018-05-01 14:51:23 +03:00
def list_os ( self ) :
2015-04-04 21:13:13 +03:00
"""
List all OSes in the DB
"""
2015-04-04 19:04:11 +03:00
sortmap = { }
2015-04-04 21:13:13 +03:00
for name , osobj in self . _all_variants . items ( ) :
sortmap [ name ] = osobj
2015-04-04 19:04:11 +03:00
2018-05-01 14:51:23 +03:00
return _sort ( sortmap )
2015-04-04 19:04:11 +03:00
2018-03-30 02:43:55 +03:00
def latest_regex ( self , regex ) :
"""
Return the latest distro name that matches the passed regex
"""
oses = [ o . name for o in self . list_os ( ) if re . match ( regex , o . name ) ]
if not oses :
return None
return oses [ 0 ]
2015-04-14 00:05:25 +03:00
def latest_fedora_version ( self ) :
2018-03-30 02:43:55 +03:00
return self . latest_regex ( " fedora[0-9]+ " )
2015-04-14 00:05:25 +03:00
2015-04-04 19:04:11 +03:00
#####################
# OsVariant classes #
#####################
2013-08-18 01:53:17 +04:00
2015-04-04 19:37:46 +03:00
class _OsVariant ( object ) :
2015-04-04 17:44:54 +03:00
def __init__ ( self , o ) :
self . _os = o
2015-04-04 19:37:46 +03:00
self . _family = self . _os and self . _os . get_family ( ) or None
2014-09-23 01:33:55 +04:00
2018-09-13 22:03:36 +03:00
self . full_id = self . _os and self . _os . get_id ( ) or None
2015-04-04 19:37:46 +03:00
self . name = self . _os and self . _os . get_short_id ( ) or " generic "
self . label = self . _os and self . _os . get_name ( ) or " Generic "
2015-11-25 05:52:06 +03:00
self . codename = self . _os and self . _os . get_codename ( ) or " "
2017-04-24 17:20:40 +03:00
self . distro = self . _os and self . _os . get_distro ( ) or " "
2014-09-23 01:33:55 +04:00
2018-09-01 15:14:33 +03:00
self . eol = self . _get_eol ( )
2015-04-04 19:37:46 +03:00
self . sortby = self . _get_sortby ( )
2015-04-04 23:08:00 +03:00
########################
# Internal helper APIs #
########################
def _is_related_to ( self , related_os_list , os = None ,
2017-09-20 10:36:27 +03:00
check_derives = True , check_upgrades = True , check_clones = True ) :
2015-04-04 23:08:00 +03:00
os = os or self . _os
if not os :
return False
if os . get_short_id ( ) in related_os_list :
return True
check_list = [ ]
def _extend ( newl ) :
for obj in newl :
if obj not in check_list :
check_list . append ( obj )
if check_derives :
_extend ( os . get_related (
libosinfo . ProductRelationship . DERIVES_FROM ) . get_elements ( ) )
if check_clones :
_extend ( os . get_related (
libosinfo . ProductRelationship . CLONES ) . get_elements ( ) )
if check_upgrades :
_extend ( os . get_related (
libosinfo . ProductRelationship . UPGRADES ) . get_elements ( ) )
for checkobj in check_list :
if ( checkobj . get_short_id ( ) in related_os_list or
self . _is_related_to ( related_os_list , os = checkobj ,
check_upgrades = check_upgrades ,
check_derives = check_derives ,
check_clones = check_clones ) ) :
return True
return False
2014-02-17 19:40:00 +04:00
2018-09-01 18:13:23 +03:00
def _get_all_devices ( self ) :
if not self . _os :
return [ ]
devlist = self . _os . get_all_devices ( )
return [ devlist . get_nth ( i ) for i in range ( devlist . get_length ( ) ) ]
def _device_filter ( self , name = None , cls = None , bus = None ) :
ret = [ ]
for dev in self . _get_all_devices ( ) :
if name and not re . match ( name , dev . get_name ( ) ) :
continue
if cls and not re . match ( cls , dev . get_class ( ) ) :
continue
if bus and not re . match ( bus , dev . get_bus_type ( ) ) :
continue
ret . append ( dev . get_name ( ) )
return ret
2014-09-09 20:39:53 +04:00
2015-04-04 17:44:54 +03:00
###############
# Cached APIs #
###############
2014-07-07 02:46:16 +04:00
2018-09-01 15:14:33 +03:00
def _get_eol ( self ) :
eol = self . _os and self . _os . get_eol_date ( ) or None
rel = self . _os and self . _os . get_release_date ( ) or None
# End of life if an EOL date is present and has past,
# or if the release date is present and was 5 years or more
if eol is not None :
now = GLib . Date ( )
now . set_time_t ( time . time ( ) )
if eol . compare ( now ) < 0 :
return True
elif rel is not None :
then = GLib . Date ( )
then . set_time_t ( time . time ( ) )
then . subtract_years ( 5 )
if rel . compare ( then ) < 0 :
return True
return False
2014-02-17 19:40:00 +04:00
def _get_sortby ( self ) :
2014-09-09 20:39:53 +04:00
if not self . _os :
return " 1 "
2014-02-17 19:40:00 +04:00
version = self . _os . get_version ( )
try :
t = version . split ( " . " )
t = t [ : min ( 4 , len ( t ) ) ] + [ 0 ] * ( 4 - min ( 4 , len ( t ) ) )
new_version = " "
for n in t :
new_version = new_version + ( " %.4i " % int ( n ) )
version = new_version
2017-07-24 11:26:48 +03:00
except Exception :
2014-02-17 19:40:00 +04:00
pass
2017-04-24 17:20:40 +03:00
return " %s - %s " % ( self . distro , version )
2014-02-17 19:40:00 +04:00
2014-09-09 20:39:53 +04:00
2015-04-04 17:44:54 +03:00
###############
# Public APIs #
###############
def is_windows ( self ) :
2018-09-01 14:55:19 +03:00
return self . _family in [ ' win9x ' , ' winnt ' , ' win16 ' ]
2015-04-04 17:44:54 +03:00
2015-11-03 19:15:26 +03:00
def broken_x2apic ( self ) :
# x2apic breaks networking in solaris10
# https://bugs.launchpad.net/bugs/1395217
2016-03-24 13:09:13 +03:00
return self . name in ( ' solaris10 ' , ' solaris11 ' )
2015-11-03 19:15:26 +03:00
2018-09-04 21:43:24 +03:00
def broken_uefi_with_hyperv ( self ) :
# Some windows versions are broken with hyperv enlightenments + UEFI
# https://bugzilla.redhat.com/show_bug.cgi?id=1185253
# https://bugs.launchpad.net/qemu/+bug/1593605
return self . name in ( " win2k8r2 " , " win7 " )
2015-04-04 17:44:54 +03:00
def get_clock ( self ) :
2015-04-04 19:37:46 +03:00
if self . is_windows ( ) or self . _family in [ ' solaris ' ] :
return " localtime "
2015-04-04 17:44:54 +03:00
return " utc "
2018-09-01 18:44:36 +03:00
def supported_netmodels ( self ) :
return self . _device_filter ( cls = " net " )
2015-04-04 17:44:54 +03:00
2016-07-29 20:17:36 +03:00
def supports_usbtablet ( self ) :
2018-09-29 20:34:48 +03:00
# If no OS specified, still default to tablet
if not self . _os :
return True
2018-09-01 18:13:23 +03:00
return bool ( self . _device_filter ( cls = " input " , name = " tablet " , bus = " usb " ) )
2015-04-04 17:44:54 +03:00
def supports_virtiodisk ( self ) :
2018-09-01 18:50:42 +03:00
return bool ( self . _device_filter ( cls = " block " , name = " virtio.*-block " ) )
2015-04-04 17:44:54 +03:00
def supports_virtionet ( self ) :
2018-09-01 18:50:42 +03:00
return bool ( self . _device_filter ( cls = " net " , name = " virtio.*-net " ) )
2015-04-04 17:44:54 +03:00
2017-03-09 00:54:16 +03:00
def supports_virtiorng ( self ) :
2018-09-01 18:50:42 +03:00
return bool ( self . _device_filter ( cls = " rng " , name = " virtio.*-rng " ) )
2015-04-04 17:44:54 +03:00
2018-09-01 20:18:49 +03:00
def supports_virtioserial ( self ) :
if self . _device_filter ( cls = " console " , name = " virtio.*-console " ) :
return True
# 2018-09-01: osinfo data is wrong for RHEL/centos here
return self . _is_related_to ( " rhel6.0 " )
2018-09-12 21:49:10 +03:00
def supports_chipset_q35 ( self ) :
return False
2014-09-24 01:09:36 +04:00
def get_recommended_resources ( self , guest ) :
2014-02-17 19:40:04 +04:00
ret = { }
2015-04-04 21:13:13 +03:00
if not self . _os :
return ret
2014-09-12 12:44:08 +04:00
def read_resource ( resources , minimum , arch ) :
# If we are reading the "minimum" block, allocate more
# resources.
ram_scale = minimum and 2 or 1
n_cpus_scale = minimum and 2 or 1
storage_scale = minimum and 2 or 1
2014-02-17 19:40:04 +04:00
for i in range ( resources . get_length ( ) ) :
r = resources . get_nth ( i )
if r . get_architecture ( ) == arch :
2014-09-12 12:44:08 +04:00
ret [ " ram " ] = r . get_ram ( ) * ram_scale
2014-02-17 19:40:04 +04:00
ret [ " cpu " ] = r . get_cpu ( )
2014-09-12 12:44:08 +04:00
ret [ " n-cpus " ] = r . get_n_cpus ( ) * n_cpus_scale
ret [ " storage " ] = r . get_storage ( ) * storage_scale
2014-02-17 19:40:04 +04:00
break
2014-09-12 12:44:08 +04:00
# libosinfo may miss the recommended resources block for some OS,
# in this case read first the minimum resources (if present)
# and use them.
read_resource ( self . _os . get_minimum_resources ( ) , True , " all " )
2014-09-24 01:09:36 +04:00
read_resource ( self . _os . get_minimum_resources ( ) , True , guest . os . arch )
2014-09-12 12:44:08 +04:00
read_resource ( self . _os . get_recommended_resources ( ) , False , " all " )
2014-09-24 01:09:36 +04:00
read_resource ( self . _os . get_recommended_resources ( ) ,
False , guest . os . arch )
2015-04-04 21:13:13 +03:00
# QEMU TCG doesn't gain anything by having extra VCPUs
if guest . type == " qemu " :
2014-09-24 01:09:36 +04:00
ret [ " n-cpus " ] = 1
2014-02-17 19:40:04 +04:00
return ret
2015-04-04 19:04:11 +03:00
OSDB = _OSDB ( )