virt-manager/virtinst/osdict.py

457 lines
13 KiB
Python
Raw Normal View History

#
# List of OS Specific data
#
# Copyright 2006-2008, 2013-2014 Red Hat, Inc.
# Jeremy Katz <katzj@redhat.com>
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import logging
import re
Redesign OS distro selection UI to be faster to use The current OS distro selection UI is fairly cumbersome to use. First you need to decide on a variant, then decide a distro and then look for the version you want. The list is filtered by default so only a subset of OS are displayed. So for less common distros you'll then need to start again and tell it to show all OS to try to find the one you want. The core problem is that we have an incredibly large list and want to make it easy for the user to find a specific entry. The modern UI paradigm for this problem is to provide interactive search with live updated results. The current UI does provide an interactive search facility on the OS version results, but you still have to first select a variant to be able to use the search which is unhelpful. This patch attempts to better apply the search UI design to the OS selection problem. We get rid of the notion of variants, distros and version, and provide a single text entry box in which the user can type a few letters of the OS name. As they type, a popover displays the matching results filtered on OS name. By default end of life OS will be hidden, so in general there will only be a small handful of results left after just typing a few characters. This makes it very quick to find and select the desired OS, without needing to provide a mutli-step navigation hierarchy. https://bugzilla.redhat.com/show_bug.cgi?id=1464306 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (crobinso: fix some pylint)
2018-05-01 14:51:23 +03:00
import time
import gi
gi.require_version('Libosinfo', '1.0')
from gi.repository import Libosinfo as libosinfo
Redesign OS distro selection UI to be faster to use The current OS distro selection UI is fairly cumbersome to use. First you need to decide on a variant, then decide a distro and then look for the version you want. The list is filtered by default so only a subset of OS are displayed. So for less common distros you'll then need to start again and tell it to show all OS to try to find the one you want. The core problem is that we have an incredibly large list and want to make it easy for the user to find a specific entry. The modern UI paradigm for this problem is to provide interactive search with live updated results. The current UI does provide an interactive search facility on the OS version results, but you still have to first select a variant to be able to use the search which is unhelpful. This patch attempts to better apply the search UI design to the OS selection problem. We get rid of the notion of variants, distros and version, and provide a single text entry box in which the user can type a few letters of the OS name. As they type, a popover displays the matching results filtered on OS name. By default end of life OS will be hidden, so in general there will only be a small handful of results left after just typing a few characters. This makes it very quick to find and select the desired OS, without needing to provide a mutli-step navigation hierarchy. https://bugzilla.redhat.com/show_bug.cgi?id=1464306 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (crobinso: fix some pylint)
2018-05-01 14:51:23 +03:00
from gi.repository import GLib
###################
# Sorting helpers #
###################
Redesign OS distro selection UI to be faster to use The current OS distro selection UI is fairly cumbersome to use. First you need to decide on a variant, then decide a distro and then look for the version you want. The list is filtered by default so only a subset of OS are displayed. So for less common distros you'll then need to start again and tell it to show all OS to try to find the one you want. The core problem is that we have an incredibly large list and want to make it easy for the user to find a specific entry. The modern UI paradigm for this problem is to provide interactive search with live updated results. The current UI does provide an interactive search facility on the OS version results, but you still have to first select a variant to be able to use the search which is unhelpful. This patch attempts to better apply the search UI design to the OS selection problem. We get rid of the notion of variants, distros and version, and provide a single text entry box in which the user can type a few letters of the OS name. As they type, a popover displays the matching results filtered on OS name. By default end of life OS will be hidden, so in general there will only be a small handful of results left after just typing a few characters. This makes it very quick to find and select the desired OS, without needing to provide a mutli-step navigation hierarchy. https://bugzilla.redhat.com/show_bug.cgi?id=1464306 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (crobinso: fix some pylint)
2018-05-01 14:51:23 +03:00
def _sort(tosort):
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
while sortby_mappings.get(sortby):
sortby = sortby + ".1"
sortby_mappings[sortby] = key
2015-04-04 21:13:13 +03:00
# Group distros by their urldistro value first, so debian is clumped
# together, and fedora, etc.
distro = osinfo.urldistro or "zzzzzzz"
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 list(distro_mappings.values()):
distro_list.sort()
distro_list.reverse()
sorted_distro_list = list(distro_mappings.keys())
sorted_distro_list.sort()
2015-04-04 21:13:13 +03:00
# Build the final list of sorted os objects
for distro in sorted_distro_list:
distro_list = distro_mappings[distro]
for key in distro_list:
orig_key = sortby_mappings[key]
retlist.append(tosort[orig_key])
return retlist
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",
"virtio26": "fedora10",
"vista": "winvista",
"winxp64": "winxp",
# Old --os-type values
"linux": "generic",
"windows": "winxp",
"solaris": "solaris10",
"unix": "freebsd9.0",
"other": "generic",
}
#################
# Internal APIs #
#################
def _make_default_variants(self):
ret = {}
# Generic variant
v = _OsVariant(None)
ret[v.name] = v
return ret
@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
self.__all_variants = allvariants
return self.__all_variants
###############
# Public APIs #
###############
def lookup_os(self, key):
key = self._aliases.get(key) or key
return self._all_variants.get(key)
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 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
Redesign OS distro selection UI to be faster to use The current OS distro selection UI is fairly cumbersome to use. First you need to decide on a variant, then decide a distro and then look for the version you want. The list is filtered by default so only a subset of OS are displayed. So for less common distros you'll then need to start again and tell it to show all OS to try to find the one you want. The core problem is that we have an incredibly large list and want to make it easy for the user to find a specific entry. The modern UI paradigm for this problem is to provide interactive search with live updated results. The current UI does provide an interactive search facility on the OS version results, but you still have to first select a variant to be able to use the search which is unhelpful. This patch attempts to better apply the search UI design to the OS selection problem. We get rid of the notion of variants, distros and version, and provide a single text entry box in which the user can type a few letters of the OS name. As they type, a popover displays the matching results filtered on OS name. By default end of life OS will be hidden, so in general there will only be a small handful of results left after just typing a few characters. This makes it very quick to find and select the desired OS, without needing to provide a mutli-step navigation hierarchy. https://bugzilla.redhat.com/show_bug.cgi?id=1464306 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (crobinso: fix some pylint)
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
"""
sortmap = {}
2015-04-04 21:13:13 +03:00
for name, osobj in self._all_variants.items():
sortmap[name] = osobj
Redesign OS distro selection UI to be faster to use The current OS distro selection UI is fairly cumbersome to use. First you need to decide on a variant, then decide a distro and then look for the version you want. The list is filtered by default so only a subset of OS are displayed. So for less common distros you'll then need to start again and tell it to show all OS to try to find the one you want. The core problem is that we have an incredibly large list and want to make it easy for the user to find a specific entry. The modern UI paradigm for this problem is to provide interactive search with live updated results. The current UI does provide an interactive search facility on the OS version results, but you still have to first select a variant to be able to use the search which is unhelpful. This patch attempts to better apply the search UI design to the OS selection problem. We get rid of the notion of variants, distros and version, and provide a single text entry box in which the user can type a few letters of the OS name. As they type, a popover displays the matching results filtered on OS name. By default end of life OS will be hidden, so in general there will only be a small handful of results left after just typing a few characters. This makes it very quick to find and select the desired OS, without needing to provide a mutli-step navigation hierarchy. https://bugzilla.redhat.com/show_bug.cgi?id=1464306 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (crobinso: fix some pylint)
2018-05-01 14:51:23 +03:00
return _sort(sortmap)
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]
def latest_fedora_version(self):
return self.latest_regex("fedora[0-9]+")
#####################
# OsVariant classes #
#####################
class _OsVariant(object):
def __init__(self, o):
self._os = o
self._family = self._os and self._os.get_family() or None
self.name = self._os and self._os.get_short_id() or "generic"
self.label = self._os and self._os.get_name() or "Generic"
self.codename = self._os and self._os.get_codename() or ""
self.distro = self._os and self._os.get_distro() or ""
self.eol = self._get_eol()
self.sortby = self._get_sortby()
self.urldistro = self._get_urldistro()
########################
# Internal helper APIs #
########################
def _is_related_to(self, related_os_list, os=None,
check_derives=True, check_upgrades=True, check_clones=True):
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
###############
# Cached APIs #
###############
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
def _get_sortby(self):
if not self._os:
return "1"
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 Exception:
pass
return "%s-%s" % (self.distro, version)
def _get_urldistro(self):
if not self._os:
return None
urldistro = self.distro
remap = {
"opensuse": "suse",
"sles": "suse",
"mes": "mandriva"
}
if remap.get(urldistro):
return remap[urldistro]
return urldistro
###############
# Public APIs #
###############
def is_windows(self):
2018-09-01 14:55:19 +03:00
return self._family in ['win9x', 'winnt', 'win16']
def broken_x2apic(self):
# x2apic breaks networking in solaris10
# https://bugs.launchpad.net/bugs/1395217
return self.name in ('solaris10', 'solaris11')
def get_clock(self):
if self.is_windows() or self._family in ['solaris']:
return "localtime"
return "utc"
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 in ["pcnet", "ne2k_pci", "rtl8139", "e1000"]:
return devname
return None
def supports_usbtablet(self):
if not self._os:
return False
fltr = libosinfo.Filter()
fltr.add_constraint("class", "input")
fltr.add_constraint("name", "tablet")
devs = self._os.get_all_devices(fltr)
for idx in range(devs.get_length()):
if devs.get_nth(idx).get_bus_type() == "usb":
return True
return False
def supports_virtiodisk(self):
if self._os:
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 self._os:
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_virtiorng(self):
if self._os:
fltr = libosinfo.Filter()
fltr.add_constraint("class", "rng")
devs = self._os.get_all_devices(fltr)
for dev in range(devs.get_length()):
d = devs.get_nth(dev)
if d.get_name() == "virtio-rng":
return True
return False
def supports_qemu_ga(self):
2016-10-24 23:34:51 +03:00
return self._is_related_to(["debian8", "fedora18", "rhel6.0", "sles11sp4"])
def default_videomodel(self, guest):
if guest.os.is_pseries():
return "vga"
if guest.has_spice() and guest.os.is_x86():
if guest.has_gl():
return "virtio"
else:
return "qxl"
if self.is_windows():
return "vga"
return None
def get_recommended_resources(self, guest):
ret = {}
2015-04-04 21:13:13 +03:00
if not self._os:
return ret
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
for i in range(resources.get_length()):
r = resources.get_nth(i)
if r.get_architecture() == arch:
ret["ram"] = r.get_ram() * ram_scale
ret["cpu"] = r.get_cpu()
ret["n-cpus"] = r.get_n_cpus() * n_cpus_scale
ret["storage"] = r.get_storage() * storage_scale
break
# 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")
read_resource(self._os.get_minimum_resources(), True, guest.os.arch)
read_resource(self._os.get_recommended_resources(), False, "all")
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":
ret["n-cpus"] = 1
return ret
OSDB = _OSDB()