1#
2# Copyright 2013, 2014, 2015 Red Hat, Inc.
3#
4# This work is licensed under the GNU GPLv2 or later.
5# See the COPYING file in the top-level directory.
6
7import os
8import weakref
9
10import libvirt
11
12from . import Capabilities
13from . import pollhelpers
14from . import support
15from . import xmlutil
16from .guest import Guest
17from .logger import log
18from .nodedev import NodeDevice
19from .storage import StoragePool, StorageVolume
20from .uri import URI, MagicURI
21
22
23def _real_local_libvirt_version():
24    """
25    Lookup the local libvirt library version, but cache the value since
26    it never changes.
27    """
28    key = "__virtinst_cached_getVersion"
29    if not hasattr(libvirt, key):
30        setattr(libvirt, key, libvirt.getVersion())
31    return getattr(libvirt, key)
32
33
34class VirtinstConnection(object):
35    """
36    Wrapper for libvirt connection that provides various bits like
37    - caching static data
38    - lookup for API feature support
39    - simplified API wrappers that handle new and old ways of doing things
40    """
41    @staticmethod
42    def get_app_cache_dir():
43        ret = os.environ.get("XDG_CACHE_HOME")
44        if not ret:
45            ret = os.path.expanduser("~/.cache")
46        return os.path.join(ret, "virt-manager")
47
48    @staticmethod
49    def in_testsuite():
50        return xmlutil.in_testsuite()
51
52    def __init__(self, uri):
53        _initial_uri = uri or ""
54
55        if MagicURI.uri_is_magic(_initial_uri):
56            self._magic_uri = MagicURI(_initial_uri)
57            self._open_uri = self._magic_uri.open_uri
58            self._uri = self._magic_uri.fakeuri or self._open_uri
59
60            self._fake_conn_predictable = self._magic_uri.predictable
61            self._fake_conn_version = self._magic_uri.conn_version
62            self._fake_libvirt_version = self._magic_uri.libvirt_version
63        else:
64            self._magic_uri = None
65            self._open_uri = _initial_uri
66            self._uri = _initial_uri
67
68            self._fake_conn_predictable = False
69            self._fake_libvirt_version = None
70            self._fake_conn_version = None
71
72        self._daemon_version = None
73        self._conn_version = None
74
75        self._libvirtconn = None
76        self._uriobj = URI(self._uri)
77        self._caps = None
78
79        self._fetch_cache = {}
80
81        # These let virt-manager register a callback which provides its
82        # own cached object lists, rather than doing fresh calls
83        self.cb_fetch_all_domains = None
84        self.cb_fetch_all_pools = None
85        self.cb_fetch_all_vols = None
86        self.cb_fetch_all_nodedevs = None
87        self.cb_cache_new_pool = None
88
89        self.support = support.SupportCache(weakref.proxy(self))
90
91
92    ##############
93    # Properties #
94    ##############
95
96    def __getattr__(self, attr):
97        # Proxy virConnect API calls
98        libvirtconn = self.__dict__.get("_libvirtconn")
99        return getattr(libvirtconn, attr)
100
101    def _get_uri(self):
102        return self._uri or self._open_uri
103    uri = property(_get_uri)
104
105    def _get_caps(self):
106        if not self._caps:
107            self._caps = Capabilities(self,
108                self._libvirtconn.getCapabilities())
109        return self._caps
110    caps = property(_get_caps)
111
112    def get_conn_for_api_arg(self):
113        return self._libvirtconn
114
115
116    ##############
117    # Public API #
118    ##############
119
120    def close(self):
121        ret = 0
122        if self._libvirtconn:
123            ret = self._libvirtconn.close()
124        self._libvirtconn = None
125        self._uri = None
126        self._fetch_cache = {}
127        return ret
128
129    def fake_conn_predictable(self):
130        return self._fake_conn_predictable
131
132    def invalidate_caps(self):
133        self._caps = None
134
135    def is_open(self):
136        return bool(self._libvirtconn)
137
138    def open(self, authcb, cbdata):
139        if self._magic_uri:
140            self._magic_uri.validate()
141
142        # Mirror the set of libvirt.c virConnectCredTypeDefault
143        valid_auth_options = [
144            libvirt.VIR_CRED_AUTHNAME,
145            libvirt.VIR_CRED_ECHOPROMPT,
146            libvirt.VIR_CRED_REALM,
147            libvirt.VIR_CRED_PASSPHRASE,
148            libvirt.VIR_CRED_NOECHOPROMPT,
149            libvirt.VIR_CRED_EXTERNAL,
150        ]
151        open_flags = 0
152
153        conn = libvirt.openAuth(self._open_uri,
154                [valid_auth_options, authcb, cbdata],
155                open_flags)
156
157        if self._magic_uri:
158            self._magic_uri.overwrite_conn_functions(conn)
159
160        self._libvirtconn = conn
161        if not self._open_uri:
162            self._uri = self._libvirtconn.getURI()
163            self._uriobj = URI(self._uri)
164
165
166    ####################
167    # Polling routines #
168    ####################
169
170    _FETCH_KEY_DOMAINS = "vms"
171    _FETCH_KEY_POOLS = "pools"
172    _FETCH_KEY_VOLS = "vols"
173    _FETCH_KEY_NODEDEVS = "nodedevs"
174
175    def _fetch_helper(self, key, raw_cb, override_cb):
176        if override_cb:
177            return override_cb()  # pragma: no cover
178        if key not in self._fetch_cache:
179            self._fetch_cache[key] = raw_cb()
180        return self._fetch_cache[key][:]
181
182    def _fetch_all_domains_raw(self):
183        dummy1, dummy2, ret = pollhelpers.fetch_vms(
184            self, {}, lambda obj, ignore: obj)
185        return [Guest(weakref.proxy(self), parsexml=obj.XMLDesc(0))
186                for obj in ret]
187
188    def _build_pool_raw(self, poolobj):
189        return StoragePool(weakref.proxy(self),
190                           parsexml=poolobj.XMLDesc(0))
191
192    def _fetch_all_pools_raw(self):
193        dummy1, dummy2, ret = pollhelpers.fetch_pools(
194            self, {}, lambda obj, ignore: obj)
195        pools = []
196        for poolobj in ret:
197            # TOCTOU race: a pool may go away in between enumeration and inspection
198            try:
199                pool = self._build_pool_raw(poolobj)
200            except libvirt.libvirtError as e:  # pragma: no cover
201                log.debug("Fetching pool XML failed: %s", e)
202                continue
203            pools.append(pool)
204        return pools
205
206    def _fetch_all_nodedevs_raw(self):
207        dummy1, dummy2, ret = pollhelpers.fetch_nodedevs(
208            self, {}, lambda obj, ignore: obj)
209        return [NodeDevice(weakref.proxy(self), obj.XMLDesc(0))
210                for obj in ret]
211
212    def _fetch_vols_raw(self, poolxmlobj):
213        ret = []
214        # TOCTOU race: a volume may go away in between enumeration and inspection
215        try:
216            pool = self._libvirtconn.storagePoolLookupByName(poolxmlobj.name)
217        except libvirt.libvirtError as e:  # pragma: no cover
218            return ret
219
220        if pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
221            return ret
222
223        dummy1, dummy2, vols = pollhelpers.fetch_volumes(
224            self, pool, {}, lambda obj, ignore: obj)
225
226        for vol in vols:
227            try:
228                xml = vol.XMLDesc(0)
229                ret.append(StorageVolume(weakref.proxy(self), parsexml=xml))
230            except libvirt.libvirtError as e:  # pragma: no cover
231                log.debug("Fetching volume XML failed: %s", e)
232        return ret
233
234    def _fetch_all_vols_raw(self):
235        ret = []
236        for poolxmlobj in self.fetch_all_pools():
237            ret.extend(self._fetch_vols_raw(poolxmlobj))
238        return ret
239
240    def _cache_new_pool_raw(self, poolobj):
241        # Make sure cache is primed
242        if self._FETCH_KEY_POOLS not in self._fetch_cache:
243            # Nothing cached yet, so next poll will pull in latest bits,
244            # so there's nothing to do
245            return
246
247        poollist = self._fetch_cache[self._FETCH_KEY_POOLS]
248        poolxmlobj = self._build_pool_raw(poolobj)
249        poollist.append(poolxmlobj)
250
251        if self._FETCH_KEY_VOLS not in self._fetch_cache:
252            return
253        vollist = self._fetch_cache[self._FETCH_KEY_VOLS]
254        vollist.extend(self._fetch_vols_raw(poolxmlobj))
255
256    def cache_new_pool(self, poolobj):
257        """
258        Insert the passed poolobj into our cache
259        """
260        if self.cb_cache_new_pool:
261            # pylint: disable=not-callable
262            return self.cb_cache_new_pool(poolobj)
263        return self._cache_new_pool_raw(poolobj)
264
265    def fetch_all_domains(self):
266        """
267        Returns a list of Guest() objects
268        """
269        return self._fetch_helper(
270                self._FETCH_KEY_DOMAINS,
271                self._fetch_all_domains_raw,
272                self.cb_fetch_all_domains)
273
274    def fetch_all_pools(self):
275        """
276        Returns a list of StoragePool objects
277        """
278        return self._fetch_helper(
279                self._FETCH_KEY_POOLS,
280                self._fetch_all_pools_raw,
281                self.cb_fetch_all_pools)
282
283    def fetch_all_vols(self):
284        """
285        Returns a list of StorageVolume objects
286        """
287        return self._fetch_helper(
288                self._FETCH_KEY_VOLS,
289                self._fetch_all_vols_raw,
290                self.cb_fetch_all_vols)
291
292    def fetch_all_nodedevs(self):
293        """
294        Returns a list of NodeDevice() objects
295        """
296        return self._fetch_helper(
297                self._FETCH_KEY_NODEDEVS,
298                self._fetch_all_nodedevs_raw,
299                self.cb_fetch_all_nodedevs)
300
301
302    #########################
303    # Libvirt API overrides #
304    #########################
305
306    def getURI(self):
307        return self._uri
308
309
310    #########################
311    # Public version checks #
312    #########################
313
314    def local_libvirt_version(self):
315        if self._fake_libvirt_version is not None:
316            return self._fake_libvirt_version
317        # This handles caching for us
318        return _real_local_libvirt_version()
319
320    def daemon_version(self):
321        if self._fake_libvirt_version is not None:
322            return self._fake_libvirt_version
323        if not self.is_remote():
324            return _real_local_libvirt_version()
325
326        if self._daemon_version is None:
327            self._daemon_version = 0
328            try:
329                self._daemon_version = self._libvirtconn.getLibVersion()
330            except Exception:  # pragma: no cover
331                log.debug("Error calling getLibVersion", exc_info=True)
332        return self._daemon_version
333
334    def conn_version(self):
335        if self._fake_conn_version is not None:
336            return self._fake_conn_version
337
338        if self._conn_version is None:
339            self._conn_version = 0
340            try:
341                self._conn_version = self._libvirtconn.getVersion()
342            except Exception:  # pragma: no cover
343                log.debug("Error calling getVersion", exc_info=True)
344        return self._conn_version
345
346
347    ###################
348    # Public URI bits #
349    ###################
350
351    def is_remote(self):
352        return bool(self._uriobj.hostname)
353    def is_privileged(self):
354        if self.get_uri_path() == "/session":
355            return False
356        if self.get_uri_path() == "/embed":
357            return os.getuid() == 0
358        return True
359    def is_unprivileged(self):
360        return not self.is_privileged()
361
362    def get_uri_hostname(self):
363        return self._uriobj.hostname
364    def get_uri_port(self):
365        return self._uriobj.port
366    def get_uri_username(self):
367        return self._uriobj.username
368    def get_uri_transport(self):
369        if self.get_uri_hostname() and not self._uriobj.transport:
370            # Libvirt defaults to transport=tls if hostname specified but
371            # no transport is specified
372            return "tls"
373        return self._uriobj.transport
374    def get_uri_path(self):
375        return self._uriobj.path
376
377    def get_uri_driver(self):
378        return self._uriobj.scheme
379
380    def is_qemu(self):
381        return self._uriobj.scheme.startswith("qemu")
382    def is_qemu_privileged(self):
383        return (self.is_qemu() and self.is_privileged())
384    def is_qemu_unprivileged(self):
385        return (self.is_qemu() and self.is_unprivileged())
386
387    def is_really_test(self):
388        return URI(self._open_uri).scheme.startswith("test")
389    def is_test(self):
390        return self._uriobj.scheme.startswith("test")
391    def is_xen(self):
392        return (self._uriobj.scheme.startswith("xen") or
393                self._uriobj.scheme.startswith("libxl"))
394    def is_lxc(self):
395        return self._uriobj.scheme.startswith("lxc")
396    def is_openvz(self):
397        return self._uriobj.scheme.startswith("openvz")
398    def is_container_only(self):
399        return self.is_lxc() or self.is_openvz()
400    def is_vz(self):
401        return (self._uriobj.scheme.startswith("vz") or
402                self._uriobj.scheme.startswith("parallels"))
403
404
405    #########################
406    # Support check helpers #
407    #########################
408
409    def support_remote_url_install(self):
410        ret = self.support.conn_stream()
411        if self._magic_uri or self.is_test():
412            ret = False
413        return self.in_testsuite() or ret
414