mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-20 14:03:52 +03:00
b93cc3bbc9
A bare 'except:' catches all exceptions [1], including SystemExit, KeyboardInterrupt, and GeneratorExit (which is not an error and should not normally be caught by user code). In situations where you need to catch all “normal” errors, you can catch the base class for all normal exceptions, Exception [2]. [1] https://docs.python.org/2/howto/doanddont.html#except [2] https://docs.python.org/2/library/exceptions.html#Exception
569 lines
17 KiB
Python
569 lines
17 KiB
Python
#
|
|
# List of OS Specific data
|
|
#
|
|
# Copyright 2006-2008, 2013-2014 Red Hat, Inc.
|
|
# 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
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# 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.
|
|
|
|
import datetime
|
|
import logging
|
|
import re
|
|
|
|
import gi
|
|
gi.require_version('Libosinfo', '1.0')
|
|
from gi.repository import Libosinfo as libosinfo
|
|
|
|
|
|
###################
|
|
# Sorting helpers #
|
|
###################
|
|
|
|
def _remove_older_point_releases(distro_list):
|
|
ret = distro_list[:]
|
|
|
|
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")
|
|
_find_latest("freebsd11")
|
|
_find_latest("centos6")
|
|
_find_latest("centos7")
|
|
return ret
|
|
|
|
|
|
def _sort(tosort, sortpref=None, limit_point_releases=False):
|
|
sortby_mappings = {}
|
|
distro_mappings = {}
|
|
retlist = []
|
|
sortpref = sortpref or []
|
|
|
|
for key, osinfo in tosort.items():
|
|
# 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
|
|
|
|
# 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 distro_mappings.values():
|
|
distro_list.sort()
|
|
distro_list.reverse()
|
|
|
|
# Move the sortpref values to the front of the list
|
|
sorted_distro_list = distro_mappings.keys()
|
|
sorted_distro_list.sort()
|
|
sortpref.reverse()
|
|
for prefer in sortpref:
|
|
if prefer not in sorted_distro_list:
|
|
continue
|
|
sorted_distro_list.remove(prefer)
|
|
sorted_distro_list.insert(0, prefer)
|
|
|
|
# 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])
|
|
|
|
# Filter out older point releases
|
|
if limit_point_releases:
|
|
retlist = _remove_older_point_releases(retlist)
|
|
|
|
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
|
|
|
|
def list_types(self):
|
|
approved_types = ["linux", "windows", "bsd", "macos",
|
|
"solaris", "other", "generic"]
|
|
return approved_types
|
|
|
|
def list_os(self, typename=None, only_supported=False, sortpref=None):
|
|
"""
|
|
List all OSes in the DB
|
|
|
|
:param typename: Only list OSes of this type
|
|
:param only_supported: Only list OSses where self.supported == True
|
|
:param sortpref: Sort these OSes at the front of the list
|
|
"""
|
|
sortmap = {}
|
|
|
|
for name, osobj in self._all_variants.items():
|
|
if typename and typename != osobj.get_typename():
|
|
continue
|
|
if only_supported and not osobj.get_supported():
|
|
continue
|
|
sortmap[name] = osobj
|
|
|
|
return _sort(sortmap, sortpref=sortpref,
|
|
limit_point_releases=only_supported)
|
|
|
|
def latest_fedora_version(self):
|
|
for osinfo in self.list_os():
|
|
if (osinfo.name.startswith("fedora") and
|
|
"unknown" not in osinfo.name):
|
|
# First fedora* occurrence should be the newest
|
|
return osinfo.name
|
|
|
|
|
|
#####################
|
|
# 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.sortby = self._get_sortby()
|
|
self.urldistro = self._get_urldistro()
|
|
self._supported = None
|
|
|
|
|
|
########################
|
|
# 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_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_supported(self):
|
|
if not self._os:
|
|
return True
|
|
|
|
eol_date = self._os.get_eol_date_string()
|
|
|
|
if eol_date:
|
|
return (datetime.datetime.strptime(eol_date, "%Y-%m-%d") >
|
|
datetime.datetime.now())
|
|
|
|
if self.name == "fedora-unknown":
|
|
return False
|
|
|
|
# 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 self._is_related_to(["fedora24", "rhel7.0", "debian6",
|
|
"ubuntu13.04", "win8", "win2k12", "mageia5", "centos7.0"],
|
|
check_clones=False, check_derives=False):
|
|
return True
|
|
return False
|
|
|
|
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 get_supported(self):
|
|
if self._supported is None:
|
|
self._supported = self._get_supported()
|
|
return self._supported
|
|
|
|
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 "bsd"
|
|
|
|
if self._family in ['darwin']:
|
|
return "macos"
|
|
|
|
return "other"
|
|
|
|
def is_windows(self):
|
|
return self.get_typename() == "windows"
|
|
|
|
def need_old_xen_disable_acpi(self):
|
|
return self._is_related_to(["winxp", "win2k"], check_upgrades=False)
|
|
|
|
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 supports_virtiommio(self):
|
|
return self._is_related_to(["fedora19"])
|
|
|
|
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):
|
|
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 = {}
|
|
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)
|
|
|
|
# QEMU TCG doesn't gain anything by having extra VCPUs
|
|
if guest.type == "qemu":
|
|
ret["n-cpus"] = 1
|
|
|
|
return ret
|
|
|
|
OSDB = _OSDB()
|