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