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>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
2013-10-28 00:59:47 +04:00
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2013-03-18 01:06:52 +04:00
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
2015-04-04 17:44:54 +03:00
import datetime
2014-09-23 01:20:07 +04:00
import re
from gi . repository import Libosinfo as libosinfo
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
2014-09-23 01:20:07 +04:00
def _remove_older_point_releases ( distro_list ) :
ret = distro_list [ : ]
2014-02-17 19:40:01 +04:00
2014-09-23 01:20:07 +04:00
def _get_minor_version ( osobj ) :
return int ( osobj . name . rsplit ( " . " , 1 ) [ - 1 ] )
def _find_latest ( prefix ) :
"""
Given a prefix like ' rhel4 ' , find the latest ' rhel4.X ' ,
and remove the rest from the os list
"""
latest_os = None
first_id = None
for osobj in ret [ : ] :
if not re . match ( " %s \ . \ d+ " % prefix , osobj . name ) :
continue
if first_id is None :
first_id = ret . index ( osobj )
ret . remove ( osobj )
if ( latest_os and
_get_minor_version ( latest_os ) > _get_minor_version ( osobj ) ) :
continue
latest_os = osobj
if latest_os :
ret . insert ( first_id , latest_os )
_find_latest ( " rhel4 " )
_find_latest ( " rhel5 " )
_find_latest ( " rhel6 " )
_find_latest ( " rhel7 " )
_find_latest ( " freebsd9 " )
_find_latest ( " freebsd10 " )
return ret
def _sort ( tosort , sortpref = None , limit_point_releases = False ) :
2013-03-18 01:06:52 +04:00
sortby_mappings = { }
distro_mappings = { }
retlist = [ ]
sortpref = sortpref or [ ]
# Make sure we are sorting by 'sortby' if specified, and group distros
# by their 'distro' tag first and foremost
for key , osinfo in tosort . items ( ) :
2013-08-11 22:52:30 +04:00
sortby = osinfo . sortby or key
2014-02-17 19:40:00 +04:00
# Hack to allow "sortby" duplicates. Remove when this never happens
# with libosinfo
while sortby_mappings . get ( sortby ) :
sortby = sortby + " .1 "
2013-03-18 01:06:52 +04:00
sortby_mappings [ sortby ] = key
2013-09-27 01:44:30 +04:00
distro = osinfo . urldistro or " zzzzzzz "
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
for distro_list in distro_mappings . values ( ) :
distro_list . sort ( )
distro_list . reverse ( )
sorted_distro_list = distro_mappings . keys ( )
sorted_distro_list . sort ( )
sortpref . reverse ( )
for prefer in sortpref :
2014-05-02 18:20:59 +04:00
if prefer not in sorted_distro_list :
2013-03-18 01:06:52 +04:00
continue
sorted_distro_list . remove ( prefer )
sorted_distro_list . insert ( 0 , prefer )
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
2014-09-23 01:20:07 +04:00
if limit_point_releases :
retlist = _remove_older_point_releases ( retlist )
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 = {
" 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 " ,
2015-04-04 19:04:11 +03:00
" vista " : " winvista " ,
" winxp64 " : " winxp " ,
2015-04-04 19:37:46 +03:00
# Old --os-type values
2015-04-04 19:04:11 +03:00
" linux " : " generic " ,
" windows " : " winxp " ,
" solaris " : " solaris10 " ,
2015-04-04 19:37:46 +03:00
" unix " : " freebsd9 " ,
" 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 #
###############
def lookup_os ( self , key ) :
key = self . _aliases . get ( key ) or key
ret = self . _all_variants . get ( key )
2015-04-04 19:37:46 +03:00
if ret is None :
2015-04-04 19:04:11 +03:00
return None
return ret
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 )
if ret and len ( ret ) > 0 and ret [ 0 ] :
return ret [ 0 ] . get_short_id ( )
return None
2015-04-04 19:37:46 +03:00
def list_types ( self ) :
approved_types = [ " linux " , " windows " , " unix " ,
" solaris " , " other " , " generic " ]
return approved_types
2015-04-04 20:48:15 +03:00
def list_os ( self , typename = None , only_supported = False , sortpref = None ) :
2015-04-04 19:04:11 +03:00
sortmap = { }
for key , osinfo in self . _all_variants . items ( ) :
2015-04-04 19:37:46 +03:00
if typename and typename != osinfo . get_typename ( ) :
2015-04-04 19:04:11 +03:00
continue
if only_supported and not osinfo . supported :
continue
sortmap [ key ] = osinfo
2015-04-04 20:48:15 +03:00
return _sort ( sortmap , sortpref = sortpref ,
limit_point_releases = only_supported )
2015-04-04 19:04:11 +03:00
#####################
# OsVariant classes #
#####################
2013-08-18 01:53:17 +04:00
2015-04-04 17:44:54 +03:00
def _is_os_related_to ( o , related_os_list ) :
if o . get_short_id ( ) in related_os_list :
return True
related = o . get_related ( libosinfo . ProductRelationship . DERIVES_FROM )
clones = o . get_related ( libosinfo . ProductRelationship . CLONES )
for r in related . get_elements ( ) + clones . get_elements ( ) :
if ( r . get_short_id ( ) in related_os_list or
_is_os_related_to ( r , related_os_list ) ) :
2014-02-17 19:40:00 +04:00
return True
2014-02-17 19:40:01 +04:00
2015-04-04 17:44:54 +03:00
return False
2014-02-17 19:40:00 +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
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 "
2014-09-23 01:33:55 +04:00
2015-04-04 19:37:46 +03:00
self . sortby = self . _get_sortby ( )
self . urldistro = self . _get_urldistro ( )
2015-04-04 17:44:54 +03:00
self . supported = self . _get_supported ( )
2014-02-17 19:40:00 +04:00
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
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
except :
pass
distro = self . _os . get_distro ( )
return " %s - %s " % ( distro , version )
def _get_supported ( self ) :
2014-09-09 20:39:53 +04:00
if not self . _os :
return True
2014-02-17 19:40:00 +04:00
d = self . _os . get_eol_date_string ( )
2014-09-07 18:49:50 +04:00
name = self . _os . get_short_id ( )
if d :
2015-04-04 17:44:54 +03:00
return ( datetime . datetime . strptime ( d , " % Y- % m- %d " ) >
datetime . datetime . now ( ) )
2014-09-07 18:49:50 +04:00
# As of libosinfo 2.11, many clearly EOL distros don't have an
# EOL date. So assume None == EOL, add some manual work arounds.
# We should fix this in a new libosinfo version, and then drop
# this hack
if name in [ " rhel7.0 " , " rhel7.1 " , " fedora19 " , " fedora20 " , " fedora21 " ,
" debian6 " , " debian7 " , " ubuntu13.04 " , " ubuntu13.10 " , " ubuntu14.04 " ,
" ubuntu14.10 " , " win8 " , " win8.1 " , " win2k12 " , " win2k12r2 " ] :
return True
return False
2014-02-17 19:40:00 +04:00
def _get_urldistro ( self ) :
2014-09-09 20:39:53 +04:00
if not self . _os :
return None
2014-02-17 19:40:00 +04:00
urldistro = self . _os . get_distro ( )
2014-02-17 19:40:01 +04:00
remap = {
" opensuse " : " suse " ,
" sles " : " suse " ,
" mes " : " mandriva "
}
if remap . get ( urldistro ) :
return remap [ urldistro ]
2014-02-17 19:40:00 +04:00
return urldistro
2014-09-09 20:39:53 +04:00
2015-04-04 17:44:54 +03:00
########################
# Internal helper APIs #
########################
def _is_related_to ( self , related_os_list ) :
if not self . _os :
return False
return _is_os_related_to ( self . _os , related_os_list )
###############
# Public APIs #
###############
2015-04-04 19:37:46 +03:00
def get_typename ( self ) :
"""
Streamline the family name for use in the virt - manager UI
"""
if not self . _os :
return " generic "
if self . _family in [ ' linux ' ] :
return " linux "
if self . _family in [ ' win9x ' , ' winnt ' , ' win16 ' ] :
return " windows "
if self . _family in [ ' solaris ' ] :
return " solaris "
if self . _family in [ ' openbsd ' , ' freebsd ' , ' netbsd ' ] :
return " unix "
return " other "
2015-04-04 17:44:54 +03:00
def is_windows ( self ) :
2015-04-04 19:37:46 +03:00
return self . get_typename ( ) == " windows "
2015-04-04 17:44:54 +03:00
def need_old_xen_disable_acpi ( self ) :
return self . _is_related_to ( [ " winxp " , " win2k " ] )
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 "
def supports_virtioconsole ( self ) :
# We used to enable this for Fedora 18+, because systemd would
# autostart a getty on /dev/hvc0 which made 'virsh console' work
# out of the box for a login prompt. However now in Fedora
# virtio-console is compiled as a module, and systemd doesn't
# detect it in time to start a getty. So the benefit of using
# it as the default is erased, and we reverted to this.
# https://bugzilla.redhat.com/show_bug.cgi?id=1039742
return False
def supports_virtiommio ( self ) :
return self . _is_related_to ( [ " fedora19 " ] )
def supports_acpi ( self , default ) :
2015-04-04 19:37:46 +03:00
if self . _family in [ ' msdos ' ] :
2015-04-04 17:44:54 +03:00
return False
return default
def supports_apic ( self , default ) :
return self . supports_acpi ( default )
def default_netmodel ( self ) :
"""
Default non - virtio net - model , since we check for that separately
"""
if not self . _os :
return None
fltr = libosinfo . Filter ( )
fltr . add_constraint ( " class " , " net " )
devs = self . _os . get_all_devices ( fltr )
for idx in range ( devs . get_length ( ) ) :
devname = devs . get_nth ( idx ) . get_name ( )
if devname != " virtio-net " :
return devname
return None
def default_inputtype ( self ) :
if self . _os :
fltr = libosinfo . Filter ( )
fltr . add_constraint ( " class " , " input " )
devs = self . _os . get_all_devices ( fltr )
if devs . get_length ( ) :
return devs . get_nth ( 0 ) . get_name ( )
return " mouse "
def default_inputbus ( self ) :
if self . _os :
fltr = libosinfo . Filter ( )
fltr . add_constraint ( " class " , " input " )
devs = self . _os . get_all_devices ( fltr )
if devs . get_length ( ) :
return devs . get_nth ( 0 ) . get_bus_type ( )
return " ps2 "
def supports_virtiodisk ( self ) :
if not self . _os :
return False
if self . _os . get_distro ( ) == " fedora " :
if self . _os . get_version ( ) == " unknown " :
return False
return int ( self . _os . get_version ( ) ) > = 10
fltr = libosinfo . Filter ( )
fltr . add_constraint ( " class " , " block " )
devs = self . _os . get_all_devices ( fltr )
for dev in range ( devs . get_length ( ) ) :
d = devs . get_nth ( dev )
if d . get_name ( ) == " virtio-block " :
return True
return False
def supports_virtionet ( self ) :
if not self . _os :
return False
if self . _os . get_distro ( ) == " fedora " :
if self . _os . get_version ( ) == " unknown " :
return False
return int ( self . _os . get_version ( ) ) > = 9
fltr = libosinfo . Filter ( )
fltr . add_constraint ( " class " , " net " )
devs = self . _os . get_all_devices ( fltr )
for dev in range ( devs . get_length ( ) ) :
d = devs . get_nth ( dev )
if d . get_name ( ) == " virtio-net " :
return True
return False
def supports_qemu_ga ( self ) :
if not self . _os :
return False
if self . name . split ( " . " ) [ 0 ] in [ " rhel7 " , " rhel6 " , " centos7 " , " centos6 " ] :
return True
if self . _os . get_distro ( ) == " fedora " :
if self . _os . get_version ( ) == " unknown " :
return False
return int ( self . _os . get_version ( ) ) > = 18
return False
def default_videomodel ( self , guest ) :
2014-09-09 20:39:53 +04:00
if guest . os . is_ppc64 ( ) and guest . os . machine == " pseries " :
return " vga "
# Marc Deslauriers of canonical had previously patched us
# to use vmvga for ubuntu, see fb76c4e5. And Fedora users report
# issues with ubuntu + qxl for as late as 14.04, so carry the vmvga
# default forward until someone says otherwise. In 2014-09 I contacted
# Marc offlist and he said this was fine for now.
if self . _os and self . _os . get_distro ( ) == " ubuntu " :
return " vmvga "
if guest . has_spice ( ) and guest . os . is_x86 ( ) :
return " qxl "
2015-04-04 17:44:54 +03:00
if self . is_windows ( ) :
2014-09-09 20:39:53 +04:00
return " vga "
return None
2014-09-24 01:09:36 +04:00
def get_recommended_resources ( self , guest ) :
2014-02-17 19:40:04 +04:00
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 )
# machvirt doesn't allow smp in non-kvm mode
if guest . type != " kvm " and guest . os . is_arm_machvirt ( ) :
ret [ " n-cpus " ] = 1
2014-02-17 19:40:04 +04:00
return ret
2015-04-04 19:04:11 +03:00
OSDB = _OSDB ( )