1#
2# Helper functions for determining if libvirt supports certain features
3#
4# Copyright 2009, 2013, 2014 Red Hat, Inc.
5#
6# This work is licensed under the GNU GPLv2 or later.
7# See the COPYING file in the top-level directory.
8
9import libvirt
10
11from . import xmlutil
12
13
14def _check_function(function, flag, run_args, data):
15    """
16    Make sure function and option flag is present in the libvirt module.
17    If run_args specified, try actually running the function against
18    the passed 'data' object
19    """
20    object_name, function_name = function.split(".")
21    classobj = getattr(libvirt, object_name, None)
22    if not classobj:
23        return False
24    if not getattr(classobj, function_name, None):
25        return False
26
27    flag_tuple = None
28    if flag:
29        found_flag = getattr(libvirt, flag, None)
30        if found_flag is None:
31            return False
32        flag_tuple = (found_flag,)
33
34    if run_args is None:
35        return None
36
37    # If function requires an object, make sure the passed obj
38    # is of the correct type
39    if not isinstance(data, classobj):
40        raise ValueError(
41            "Passed obj %s with args must be of type %s, was %s" %
42            (data, str(classobj), type(data)))
43
44    use_args = run_args
45    if flag_tuple:
46        use_args += flag_tuple
47
48    try:
49        getattr(data, function_name)(*run_args)
50    except libvirt.libvirtError as e:
51        if SupportCache.is_error_nosupport(e):
52            return False
53        if bool(flag_tuple):  # pragma: no cover
54            return False
55    except Exception as e:  # pragma: no cover
56        # Other python exceptions likely mean the bindings are horked
57        return False
58    return True
59
60
61def _version_str_to_int(verstr):
62    if verstr is None:
63        return None
64    if verstr == 0:
65        return 0
66
67    if verstr.count(".") != 2:
68        raise xmlutil.DevError(
69                "version string '%s' needs two '.' in it." % verstr)
70
71    return ((int(verstr.split(".")[0]) * 1000000) +
72            (int(verstr.split(".")[1]) * 1000) + (int(verstr.split(".")[2])))
73
74
75class _SupportCheck(object):
76    """
77    @version: Minimum libvirt version required for this feature. Not used
78        if 'args' provided.
79
80    @function: Function name to check exists. Expected to be of the
81        format $obj.$func. Like virDomain.isActive
82
83    @run_args: Argument tuple to actually test 'function' with, and check
84        for an 'unsupported' error from libvirt.
85
86    @flag: A flag to check exists. This will be appended to the argument
87        :list if run_args are provided, otherwise we will only check against
88        that the flag is present in the python bindings.
89
90    @hv_version: A dictionary with hypervisor names for keys, and
91        hypervisor versions as values. This is for saying 'this feature
92        is only supported with qemu version 1.5.0' or similar. If the
93        version is 0, then perform no version check.
94
95    @hv_libvirt_version: Similar to hv_version, but this will check
96        the version of libvirt for a specific hv key. Use this to say
97        'this feature is supported with qemu and libvirt version 1.0.0,
98         and xen with libvirt version 1.1.0'
99    """
100    def __init__(self,
101                 function=None, run_args=None, flag=None,
102                 version=None, hv_version=None, hv_libvirt_version=None):
103        self.function = function
104        self.run_args = run_args
105        self.flag = flag
106        self.version = version
107        self.hv_version = hv_version or {}
108        self.hv_libvirt_version = hv_libvirt_version or {}
109
110        if self.function:
111            assert len(function.split(".")) == 2
112
113        versions = ([self.version] + list(self.hv_libvirt_version.values()))
114        for vstr in versions:
115            v = _version_str_to_int(vstr)
116            if vstr is not None and v != 0 and v < 7003:
117                raise xmlutil.DevError(
118                    "Cannot enforce "
119                    "support checks for libvirt versions less than 0.7.3, "
120                    "since required APIs were not available. ver=%s" % vstr)
121
122    def __call__(self, virtconn, data=None):
123        """
124        Attempt to determine if a specific libvirt feature is support given
125        the passed connection.
126
127        :param virtconn: VirtinstConnection to check feature on
128        :param feature: Feature type to check support for
129        :type feature: One of the SUPPORT_* flags
130        :param data: Option libvirt object to use in feature checking
131        :type data: Could be virDomain, virNetwork, virStoragePool, hv name, etc
132
133        :returns: True if feature is supported, False otherwise
134        """
135        if "VirtinstConnection" in repr(data):
136            data = data.get_conn_for_api_arg()
137
138        if self.function:
139            ret = _check_function(
140                    self.function, self.flag, self.run_args, data)
141            if ret is not None:
142                return ret
143
144        # Do this after the function check, since there's an ordering issue
145        # with VirtinstConnection
146        hv_type = virtconn.get_uri_driver()
147        actual_libvirt_version = virtconn.daemon_version()
148        actual_hv_version = virtconn.conn_version()
149
150        # Check that local libvirt version is sufficient
151        v = _version_str_to_int(self.version)
152        if v and (v > actual_libvirt_version):
153            return False
154
155        if self.hv_version:
156            if hv_type not in self.hv_version:
157                if "all" not in self.hv_version:
158                    return False
159            elif (actual_hv_version <
160                  _version_str_to_int(self.hv_version[hv_type])):
161                return False
162
163        if self.hv_libvirt_version:
164            if hv_type not in self.hv_libvirt_version:
165                if "all" not in self.hv_libvirt_version:
166                    return False
167            elif (actual_libvirt_version <
168                  _version_str_to_int(self.hv_libvirt_version[hv_type])):
169                return False
170
171        return True
172
173
174def _make(*args, **kwargs):
175    """
176    Create a _SupportCheck from the passed args, then turn it into a
177    SupportCache method which captures and caches the returned support
178    value in self._cache
179    """
180    # pylint: disable=protected-access
181    support_obj = _SupportCheck(*args, **kwargs)
182
183    def cache_wrapper(self, data=None):
184        if support_obj not in self._cache:
185            support_ret = support_obj(self._virtconn, data or self._virtconn)
186            self._cache[support_obj] = support_ret
187        return self._cache[support_obj]
188
189    return cache_wrapper
190
191
192class SupportCache:
193    """
194    Class containing all support checks and access APIs, and support for
195    caching returned results
196    """
197
198    @staticmethod
199    def is_libvirt_error_no_domain(err):
200        """
201        Small helper to check if the passed exception is a libvirt error
202        with code VIR_ERR_NO_DOMAIN
203        """
204        if not isinstance(err, libvirt.libvirtError):
205            return False  # pragma: no cover
206        return err.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN
207
208    @staticmethod
209    def is_error_nosupport(err):
210        """
211        Check if passed exception indicates that the called libvirt command isn't
212        supported
213
214        :param err: Exception raised from command call
215        :returns: True if command isn't supported, False if we can't determine
216        """
217        if not isinstance(err, libvirt.libvirtError):
218            return False  # pragma: no cover
219
220        if (err.get_error_code() == libvirt.VIR_ERR_RPC or
221            err.get_error_code() == libvirt.VIR_ERR_NO_SUPPORT):
222            return True
223
224        return False  # pragma: no cover
225
226
227    def __init__(self, virtconn):
228        self._cache = {}
229        self._virtconn = virtconn
230
231    conn_domain = _make(
232        function="virConnect.listAllDomains", run_args=())
233    conn_storage = _make(
234        function="virConnect.listAllStoragePools", run_args=())
235    conn_nodedev = _make(
236        function="virConnect.listDevices", run_args=(None, 0))
237    conn_network = _make(
238        function="virConnect.listNetworks", run_args=())
239
240    conn_stream = _make(function="virConnect.newStream", run_args=(0,))
241    conn_working_xen_events = _make(hv_version={"xen": "4.0.0", "all": 0})
242    # This is an arbitrary check to say whether it's a good idea to
243    # default to qcow2. It might be fine for xen or qemu older than the versions
244    # here, but until someone tests things I'm going to be a bit conservative.
245    conn_default_qcow2 = _make(hv_version={"qemu": "1.2.0", "test": 0})
246    conn_autosocket = _make(hv_libvirt_version={"qemu": "1.0.6"})
247    conn_pm_disable = _make(hv_version={"qemu": "1.2.0", "test": 0})
248    conn_qcow2_lazy_refcounts = _make(
249        version="1.1.0", hv_version={"qemu": "1.2.0", "test": 0})
250    conn_hyperv_vapic = _make(
251        version="1.1.0", hv_version={"qemu": "1.1.0", "test": 0})
252    conn_hyperv_clock = _make(
253        version="1.2.2", hv_version={"qemu": "1.5.3", "test": 0})
254    conn_domain_capabilities = _make(
255        function="virConnect.getDomainCapabilities",
256        run_args=(None, None, None, None))
257    conn_vmport = _make(
258        version="1.2.16", hv_version={"qemu": "2.2.0", "test": 0})
259    conn_mem_stats_period = _make(
260        function="virDomain.setMemoryStatsPeriod",
261        version="1.1.1", hv_version={"qemu": 0, "test": "5.6.0"})
262    # spice GL is actually enabled with libvirt 1.3.3, but 3.1.0 is the
263    # first version that sorts out the qemu:///system + cgroup issues
264    conn_graphics_listen_none = _make(version="2.0.0")
265    conn_rng_urandom = _make(version="1.3.4")
266    conn_usb3_ports = _make(version="1.3.5")
267    conn_machvirt_pci_default = _make(version="3.0.0")
268    conn_qemu_xhci = _make(version="3.3.0", hv_version={"qemu": "2.9.0"})
269    conn_vnc_none_auth = _make(hv_version={"qemu": "2.9.0"})
270    conn_device_boot_order = _make(hv_version={"qemu": 0, "test": 0})
271    conn_riscv_virt_pci_default = _make(version="5.3.0", hv_version={"qemu": "4.0.0"})
272
273    # We choose qemu 2.11.0 as the first version to target for q35 default.
274    # That's not really based on anything except reasonably modern at the
275    # time of these patches.
276    qemu_q35_default = _make(hv_version={"qemu": "2.11.0", "test": "0"})
277
278    # This is for disk <driver name=qemu>. xen supports this, but it's
279    # limited to arbitrary new enough xen, since I know libxl can handle it
280    # but I don't think the old xend driver does.
281    conn_disk_driver_name_qemu = _make(
282        hv_version={"qemu": 0, "xen": "4.2.0"},
283        hv_libvirt_version={"qemu": 0, "xen": "1.1.0"})
284
285    # Domain checks
286    domain_xml_inactive = _make(function="virDomain.XMLDesc", run_args=(),
287        flag="VIR_DOMAIN_XML_INACTIVE")
288    domain_xml_secure = _make(function="virDomain.XMLDesc", run_args=(),
289        flag="VIR_DOMAIN_XML_SECURE")
290    domain_managed_save = _make(
291        function="virDomain.hasManagedSaveImage",
292        run_args=(0,))
293    domain_job_info = _make(function="virDomain.jobInfo", run_args=())
294    domain_list_snapshots = _make(
295        function="virDomain.listAllSnapshots", run_args=())
296    domain_memory_stats = _make(
297        function="virDomain.memoryStats", run_args=())
298    domain_state = _make(function="virDomain.state", run_args=())
299    domain_open_graphics = _make(function="virDomain.openGraphicsFD",
300        version="1.2.8", hv_version={"qemu": 0})
301
302    # Pool checks
303    pool_metadata_prealloc = _make(
304        flag="VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA",
305        version="1.0.1")
306
307
308    def _check_version(self, version):
309        """
310        Check libvirt version. Useful for the test suite so we don't need
311        to keep adding new support checks.
312        """
313        sobj = _SupportCheck(version=version)
314        return sobj(self._virtconn, None)
315