2013-03-18 01:06:52 +04:00
#
# Helper functions for determining if libvirt supports certain features
#
2014-03-13 15:52:51 +04:00
# Copyright 2009, 2013, 2014 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
#
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
import libvirt
2020-07-18 01:58:00 +03:00
from . import xmlutil
2013-03-18 01:06:52 +04:00
2015-02-18 23:16:02 +03:00
def _check_function ( function , flag , run_args , data ) :
2019-06-14 20:31:31 +03:00
"""
Make sure function and option flag is present in the libvirt module .
If run_args specified , try actually running the function against
the passed ' data ' object
"""
object_name , function_name = function . split ( " . " )
classobj = getattr ( libvirt , object_name , None )
if not classobj :
return False
if not getattr ( classobj , function_name , None ) :
2014-02-11 02:02:22 +04:00
return False
2019-06-14 20:31:31 +03:00
flag_tuple = None
2014-02-11 02:02:22 +04:00
if flag :
2019-06-14 20:31:31 +03:00
found_flag = getattr ( libvirt , flag , None )
if found_flag is None :
2014-02-11 02:02:22 +04:00
return False
flag_tuple = ( found_flag , )
2015-02-18 23:16:02 +03:00
if run_args is None :
2014-02-11 02:02:22 +04:00
return None
# If function requires an object, make sure the passed obj
# is of the correct type
2019-06-14 20:31:31 +03:00
if not isinstance ( data , classobj ) :
raise ValueError (
" Passed obj %s with args must be of type %s , was %s " %
( data , str ( classobj ) , type ( data ) ) )
2014-02-11 02:02:22 +04:00
2019-06-14 20:31:31 +03:00
use_args = run_args
if flag_tuple :
use_args + = flag_tuple
2014-02-11 02:02:22 +04:00
2019-06-14 20:31:31 +03:00
try :
getattr ( data , function_name ) ( * run_args )
except libvirt . libvirtError as e :
if SupportCache . is_error_nosupport ( e ) :
return False
if bool ( flag_tuple ) : # pragma: no cover
return False
2021-10-04 22:38:24 +03:00
except Exception : # pragma: no cover
2019-06-14 20:31:31 +03:00
# Other python exceptions likely mean the bindings are horked
return False
return True
2014-02-11 02:02:22 +04:00
2014-02-11 01:53:47 +04:00
def _version_str_to_int ( verstr ) :
if verstr is None :
return None
if verstr == 0 :
return 0
if verstr . count ( " . " ) != 2 :
2020-07-18 01:58:00 +03:00
raise xmlutil . DevError (
" version string ' %s ' needs two ' . ' in it. " % verstr )
2014-02-11 01:53:47 +04:00
return ( ( int ( verstr . split ( " . " ) [ 0 ] ) * 1000000 ) +
( int ( verstr . split ( " . " ) [ 1 ] ) * 1000 ) + ( int ( verstr . split ( " . " ) [ 2 ] ) ) )
2013-08-09 23:56:18 +04:00
class _SupportCheck ( object ) :
"""
@version : Minimum libvirt version required for this feature . Not used
2014-02-11 02:12:24 +04:00
if ' args ' provided .
2019-06-14 20:31:31 +03:00
@function : Function name to check exists . Expected to be of the
format $ obj . $ func . Like virDomain . isActive
2014-02-11 02:12:24 +04:00
@run_args : Argument tuple to actually test ' function ' with , and check
for an ' unsupported ' error from libvirt .
2013-08-09 23:56:18 +04:00
@flag : A flag to check exists . This will be appended to the argument
2019-03-06 21:05:47 +03:00
: list if run_args are provided , otherwise we will only check against
2014-02-11 02:12:24 +04:00
that the flag is present in the python bindings .
@hv_version : A dictionary with hypervisor names for keys , and
hypervisor versions as values . This is for saying ' this feature
is only supported with qemu version 1.5 .0 ' or similar. If the
version is 0 , then perform no version check .
@hv_libvirt_version : Similar to hv_version , but this will check
the version of libvirt for a specific hv key . Use this to say
' this feature is supported with qemu and libvirt version 1.0.0,
and xen with libvirt version 1.1 .0 '
2013-08-09 23:56:18 +04:00
"""
def __init__ ( self ,
2014-02-11 02:12:24 +04:00
function = None , run_args = None , flag = None ,
version = None , hv_version = None , hv_libvirt_version = None ) :
2013-08-09 23:56:18 +04:00
self . function = function
2014-02-11 02:12:24 +04:00
self . run_args = run_args
2013-08-09 23:56:18 +04:00
self . flag = flag
2014-02-11 01:53:47 +04:00
self . version = version
2014-02-11 02:12:24 +04:00
self . hv_version = hv_version or { }
self . hv_libvirt_version = hv_libvirt_version or { }
2013-08-09 23:56:18 +04:00
2019-06-14 20:31:31 +03:00
if self . function :
assert len ( function . split ( " . " ) ) == 2
2017-10-11 14:35:46 +03:00
versions = ( [ self . version ] + list ( self . hv_libvirt_version . values ( ) ) )
2014-02-11 01:53:47 +04:00
for vstr in versions :
v = _version_str_to_int ( vstr )
2019-03-06 21:05:47 +03:00
if vstr is not None and v != 0 and v < 7003 :
2020-07-18 01:58:00 +03:00
raise xmlutil . DevError (
" Cannot enforce "
2019-03-06 21:05:47 +03:00
" support checks for libvirt versions less than 0.7.3, "
2014-02-11 01:53:47 +04:00
" since required APIs were not available. ver= %s " % vstr )
2014-02-11 01:37:17 +04:00
2019-06-07 21:48:59 +03:00
def __call__ ( self , virtconn , data = None ) :
"""
Attempt to determine if a specific libvirt feature is support given
the passed connection .
: param virtconn : VirtinstConnection to check feature on
: param feature : Feature type to check support for
: type feature : One of the SUPPORT_ * flags
: param data : Option libvirt object to use in feature checking
: type data : Could be virDomain , virNetwork , virStoragePool , hv name , etc
: returns : True if feature is supported , False otherwise
"""
if " VirtinstConnection " in repr ( data ) :
data = data . get_conn_for_api_arg ( )
2019-06-14 20:31:31 +03:00
if self . function :
ret = _check_function (
self . function , self . flag , self . run_args , data )
if ret is not None :
return ret
2013-08-09 23:56:18 +04:00
# Do this after the function check, since there's an ordering issue
2018-03-20 19:18:35 +03:00
# with VirtinstConnection
2019-06-07 21:48:59 +03:00
hv_type = virtconn . get_uri_driver ( )
actual_libvirt_version = virtconn . daemon_version ( )
actual_hv_version = virtconn . conn_version ( )
2013-08-09 23:56:18 +04:00
# Check that local libvirt version is sufficient
2017-10-11 14:35:54 +03:00
v = _version_str_to_int ( self . version )
if v and ( v > actual_libvirt_version ) :
2013-08-09 23:56:18 +04:00
return False
2014-02-11 02:12:24 +04:00
if self . hv_version :
if hv_type not in self . hv_version :
if " all " not in self . hv_version :
2014-02-11 01:14:39 +04:00
return False
2014-02-11 02:12:24 +04:00
elif ( actual_hv_version <
_version_str_to_int ( self . hv_version [ hv_type ] ) ) :
2013-08-09 23:56:18 +04:00
return False
2014-02-11 02:12:24 +04:00
if self . hv_libvirt_version :
if hv_type not in self . hv_libvirt_version :
2017-12-18 21:01:30 +03:00
if " all " not in self . hv_libvirt_version :
2014-02-11 01:14:39 +04:00
return False
2014-02-11 02:12:24 +04:00
elif ( actual_libvirt_version <
_version_str_to_int ( self . hv_libvirt_version [ hv_type ] ) ) :
2013-08-09 23:56:18 +04:00
return False
return True
2019-06-07 21:48:59 +03:00
def _make ( * args , * * kwargs ) :
"""
Create a _SupportCheck from the passed args , then turn it into a
SupportCache method which captures and caches the returned support
value in self . _cache
"""
# pylint: disable=protected-access
support_obj = _SupportCheck ( * args , * * kwargs )
2013-08-09 23:56:18 +04:00
2019-06-07 21:48:59 +03:00
def cache_wrapper ( self , data = None ) :
if support_obj not in self . _cache :
support_ret = support_obj ( self . _virtconn , data or self . _virtconn )
self . _cache [ support_obj ] = support_ret
return self . _cache [ support_obj ]
2013-08-09 23:56:18 +04:00
2019-06-07 21:48:59 +03:00
return cache_wrapper
2013-03-18 01:06:52 +04:00
2018-02-14 15:17:31 +03:00
2019-06-07 21:45:25 +03:00
class SupportCache :
2013-03-18 01:06:52 +04:00
"""
2019-06-07 21:48:59 +03:00
Class containing all support checks and access APIs , and support for
caching returned results
2016-01-27 04:00:38 +03:00
"""
2019-06-07 23:39:33 +03:00
@staticmethod
def is_libvirt_error_no_domain ( err ) :
"""
Small helper to check if the passed exception is a libvirt error
with code VIR_ERR_NO_DOMAIN
"""
if not isinstance ( err , libvirt . libvirtError ) :
2019-06-14 20:31:31 +03:00
return False # pragma: no cover
2019-06-07 23:39:33 +03:00
return err . get_error_code ( ) == libvirt . VIR_ERR_NO_DOMAIN
2019-06-07 23:48:21 +03:00
@staticmethod
def is_error_nosupport ( err ) :
"""
Check if passed exception indicates that the called libvirt command isn ' t
supported
: param err : Exception raised from command call
: returns : True if command isn ' t supported, False if we can ' t determine
"""
if not isinstance ( err , libvirt . libvirtError ) :
2019-06-14 20:31:31 +03:00
return False # pragma: no cover
2019-06-07 23:48:21 +03:00
if ( err . get_error_code ( ) == libvirt . VIR_ERR_RPC or
err . get_error_code ( ) == libvirt . VIR_ERR_NO_SUPPORT ) :
return True
2019-06-14 20:31:31 +03:00
return False # pragma: no cover
2019-06-07 23:48:21 +03:00
2019-06-07 23:39:33 +03:00
2019-06-07 21:48:59 +03:00
def __init__ ( self , virtconn ) :
self . _cache = { }
self . _virtconn = virtconn
2019-06-07 21:45:25 +03:00
2020-01-24 23:16:19 +03:00
conn_domain = _make (
2020-01-23 01:59:35 +03:00
function = " virConnect.listAllDomains " , run_args = ( ) )
2019-06-07 21:48:59 +03:00
conn_storage = _make (
2020-01-23 01:59:35 +03:00
function = " virConnect.listAllStoragePools " , run_args = ( ) )
2019-06-07 21:48:59 +03:00
conn_nodedev = _make (
2019-06-07 21:45:25 +03:00
function = " virConnect.listDevices " , run_args = ( None , 0 ) )
2020-01-24 23:16:19 +03:00
conn_network = _make (
function = " virConnect.listNetworks " , run_args = ( ) )
2019-06-07 21:48:59 +03:00
conn_stream = _make ( function = " virConnect.newStream " , run_args = ( 0 , ) )
conn_working_xen_events = _make ( hv_version = { " xen " : " 4.0.0 " , " all " : 0 } )
2019-06-07 21:45:25 +03:00
# This is an arbitrary check to say whether it's a good idea to
# default to qcow2. It might be fine for xen or qemu older than the versions
# here, but until someone tests things I'm going to be a bit conservative.
2019-06-07 21:48:59 +03:00
conn_default_qcow2 = _make ( hv_version = { " qemu " : " 1.2.0 " , " test " : 0 } )
conn_autosocket = _make ( hv_libvirt_version = { " qemu " : " 1.0.6 " } )
conn_pm_disable = _make ( hv_version = { " qemu " : " 1.2.0 " , " test " : 0 } )
conn_qcow2_lazy_refcounts = _make (
2019-06-07 21:45:25 +03:00
version = " 1.1.0 " , hv_version = { " qemu " : " 1.2.0 " , " test " : 0 } )
2019-06-07 21:48:59 +03:00
conn_hyperv_vapic = _make (
2019-06-07 21:45:25 +03:00
version = " 1.1.0 " , hv_version = { " qemu " : " 1.1.0 " , " test " : 0 } )
2019-06-07 21:48:59 +03:00
conn_hyperv_clock = _make (
2019-06-07 21:45:25 +03:00
version = " 1.2.2 " , hv_version = { " qemu " : " 1.5.3 " , " test " : 0 } )
2019-06-07 21:48:59 +03:00
conn_domain_capabilities = _make (
2019-06-07 21:45:25 +03:00
function = " virConnect.getDomainCapabilities " ,
run_args = ( None , None , None , None ) )
2019-06-07 21:48:59 +03:00
conn_vmport = _make (
2019-06-07 21:45:25 +03:00
version = " 1.2.16 " , hv_version = { " qemu " : " 2.2.0 " , " test " : 0 } )
2019-06-07 21:48:59 +03:00
conn_mem_stats_period = _make (
2019-06-07 21:45:25 +03:00
function = " virDomain.setMemoryStatsPeriod " ,
2020-09-05 22:50:04 +03:00
version = " 1.1.1 " , hv_version = { " qemu " : 0 , " test " : " 5.6.0 " } )
2019-06-07 21:45:25 +03:00
# spice GL is actually enabled with libvirt 1.3.3, but 3.1.0 is the
# first version that sorts out the qemu:///system + cgroup issues
2019-06-07 21:48:59 +03:00
conn_graphics_listen_none = _make ( version = " 2.0.0 " )
conn_rng_urandom = _make ( version = " 1.3.4 " )
conn_usb3_ports = _make ( version = " 1.3.5 " )
conn_machvirt_pci_default = _make ( version = " 3.0.0 " )
conn_qemu_xhci = _make ( version = " 3.3.0 " , hv_version = { " qemu " : " 2.9.0 " } )
conn_vnc_none_auth = _make ( hv_version = { " qemu " : " 2.9.0 " } )
conn_device_boot_order = _make ( hv_version = { " qemu " : 0 , " test " : 0 } )
conn_riscv_virt_pci_default = _make ( version = " 5.3.0 " , hv_version = { " qemu " : " 4.0.0 " } )
2019-06-07 21:45:25 +03:00
# We choose qemu 2.11.0 as the first version to target for q35 default.
# That's not really based on anything except reasonably modern at the
# time of these patches.
2019-06-07 21:48:59 +03:00
qemu_q35_default = _make ( hv_version = { " qemu " : " 2.11.0 " , " test " : " 0 " } )
2019-06-07 21:45:25 +03:00
# This is for disk <driver name=qemu>. xen supports this, but it's
# limited to arbitrary new enough xen, since I know libxl can handle it
# but I don't think the old xend driver does.
2019-06-07 21:48:59 +03:00
conn_disk_driver_name_qemu = _make (
2019-06-07 21:45:25 +03:00
hv_version = { " qemu " : 0 , " xen " : " 4.2.0 " } ,
hv_libvirt_version = { " qemu " : 0 , " xen " : " 1.1.0 " } )
# Domain checks
2019-06-07 21:48:59 +03:00
domain_xml_inactive = _make ( function = " virDomain.XMLDesc " , run_args = ( ) ,
2019-06-07 21:45:25 +03:00
flag = " VIR_DOMAIN_XML_INACTIVE " )
2019-06-07 21:48:59 +03:00
domain_xml_secure = _make ( function = " virDomain.XMLDesc " , run_args = ( ) ,
2019-06-07 21:45:25 +03:00
flag = " VIR_DOMAIN_XML_SECURE " )
2019-06-07 21:48:59 +03:00
domain_managed_save = _make (
2019-06-07 21:45:25 +03:00
function = " virDomain.hasManagedSaveImage " ,
run_args = ( 0 , ) )
2019-06-07 21:48:59 +03:00
domain_job_info = _make ( function = " virDomain.jobInfo " , run_args = ( ) )
domain_list_snapshots = _make (
2019-06-07 21:45:25 +03:00
function = " virDomain.listAllSnapshots " , run_args = ( ) )
2019-06-07 21:48:59 +03:00
domain_memory_stats = _make (
2019-06-07 21:45:25 +03:00
function = " virDomain.memoryStats " , run_args = ( ) )
2019-06-07 21:48:59 +03:00
domain_state = _make ( function = " virDomain.state " , run_args = ( ) )
domain_open_graphics = _make ( function = " virDomain.openGraphicsFD " ,
2019-06-07 21:45:25 +03:00
version = " 1.2.8 " , hv_version = { " qemu " : 0 } )
# Pool checks
2019-06-07 21:48:59 +03:00
pool_metadata_prealloc = _make (
2019-06-07 21:45:25 +03:00
flag = " VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA " ,
version = " 1.0.1 " )
2019-06-07 21:48:59 +03:00
def _check_version ( self , version ) :
2019-06-07 21:45:25 +03:00
"""
Check libvirt version . Useful for the test suite so we don ' t need
to keep adding new support checks .
"""
sobj = _SupportCheck ( version = version )
2019-06-07 21:48:59 +03:00
return sobj ( self . _virtconn , None )