1# 2# Some code for parsing libvirt's capabilities XML 3# 4# Copyright 2007, 2012-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 pwd 10 11from .domain import DomainCpu 12from .logger import log 13from .xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty 14 15 16################################### 17# capabilities host <cpu> parsing # 18################################### 19 20class _CapsCPU(DomainCpu): 21 arch = XMLProperty("./arch") 22 23 24########################### 25# Caps <topology> parsers # 26########################### 27 28class _CapsTopologyCPU(XMLBuilder): 29 XML_NAME = "cpu" 30 id = XMLProperty("./@id") 31 32 33class _TopologyCell(XMLBuilder): 34 XML_NAME = "cell" 35 cpus = XMLChildProperty(_CapsTopologyCPU, relative_xpath="./cpus") 36 37 38class _CapsTopology(XMLBuilder): 39 XML_NAME = "topology" 40 cells = XMLChildProperty(_TopologyCell, relative_xpath="./cells") 41 42 43###################################### 44# Caps <host> and <secmodel> parsers # 45###################################### 46 47class _CapsSecmodelBaselabel(XMLBuilder): 48 XML_NAME = "baselabel" 49 type = XMLProperty("./@type") 50 content = XMLProperty(".") 51 52 53class _CapsSecmodel(XMLBuilder): 54 XML_NAME = "secmodel" 55 model = XMLProperty("./model") 56 baselabels = XMLChildProperty(_CapsSecmodelBaselabel) 57 58 59class _CapsHost(XMLBuilder): 60 XML_NAME = "host" 61 secmodels = XMLChildProperty(_CapsSecmodel) 62 cpu = XMLChildProperty(_CapsCPU, is_single=True) 63 topology = XMLChildProperty(_CapsTopology, is_single=True) 64 65 def get_qemu_baselabel(self): 66 for secmodel in self.secmodels: 67 if secmodel.model != "dac": 68 continue 69 70 label = None 71 for baselabel in secmodel.baselabels: 72 if baselabel.type in ["qemu", "kvm"]: 73 label = baselabel.content 74 break 75 if not label: 76 continue # pragma: no cover 77 78 # XML we are looking at is like: 79 # 80 # <secmodel> 81 # <model>dac</model> 82 # <doi>0</doi> 83 # <baselabel type='kvm'>+107:+107</baselabel> 84 # <baselabel type='qemu'>+107:+107</baselabel> 85 # </secmodel> 86 try: 87 uid = int(label.split(":")[0].replace("+", "")) 88 user = pwd.getpwuid(uid)[0] 89 return user, uid 90 except Exception: 91 log.debug("Exception parsing qemu dac baselabel=%s", 92 label, exc_info=True) 93 return None, None 94 95 96################################ 97# <guest> and <domain> parsers # 98################################ 99 100class _CapsMachine(XMLBuilder): 101 XML_NAME = "machine" 102 name = XMLProperty(".") 103 canonical = XMLProperty("./@canonical") 104 105 106class _CapsDomain(XMLBuilder): 107 XML_NAME = "domain" 108 hypervisor_type = XMLProperty("./@type") 109 emulator = XMLProperty("./emulator") 110 machines = XMLChildProperty(_CapsMachine) 111 112 113class _CapsGuestFeatures(XMLBuilder): 114 XML_NAME = "features" 115 116 pae = XMLProperty("./pae", is_bool=True) 117 acpi = XMLProperty("./acpi/@default", is_onoff=True) 118 apic = XMLProperty("./apic/@default", is_onoff=True) 119 120 121class _CapsGuest(XMLBuilder): 122 XML_NAME = "guest" 123 124 os_type = XMLProperty("./os_type") 125 arch = XMLProperty("./arch/@name") 126 loader = XMLProperty("./arch/loader") 127 emulator = XMLProperty("./arch/emulator") 128 129 domains = XMLChildProperty(_CapsDomain, relative_xpath="./arch") 130 features = XMLChildProperty(_CapsGuestFeatures, is_single=True) 131 machines = XMLChildProperty(_CapsMachine, relative_xpath="./arch") 132 133 134 ############### 135 # Public APIs # 136 ############### 137 138 def all_machine_names(self, domain): 139 """ 140 Return all machine string names, including canonical aliases for 141 the guest+domain combo but avoiding duplicates 142 """ 143 mobjs = (domain and domain.machines) or self.machines 144 ret = [] 145 for m in mobjs: 146 ret.append(m.name) 147 if m.canonical and m.canonical not in ret: 148 ret.append(m.canonical) 149 return ret 150 151 def is_kvm_available(self): 152 """ 153 Return True if kvm guests can be installed 154 """ 155 for d in self.domains: 156 if d.hypervisor_type == "kvm": 157 return True 158 return False 159 160 def supports_pae(self): 161 """ 162 Return True if capabilities report support for PAE 163 """ 164 return bool(self.features.pae) 165 166 def supports_acpi(self): 167 """ 168 Return Tree if capabilities report support for ACPI 169 """ 170 return bool(self.features.acpi) 171 172 def supports_apic(self): 173 """ 174 Return Tree if capabilities report support for APIC 175 """ 176 return bool(self.features.apic) 177 178 179############################ 180# Main capabilities object # 181############################ 182 183class _CapsInfo(object): 184 """ 185 Container object to hold the results of guest_lookup, so users don't 186 need to juggle two objects 187 """ 188 def __init__(self, conn, guest, domain): 189 self.conn = conn 190 self.guest = guest 191 self.domain = domain 192 193 self.hypervisor_type = self.domain.hypervisor_type 194 self.os_type = self.guest.os_type 195 self.arch = self.guest.arch 196 self.loader = self.guest.loader 197 198 self.emulator = self.domain.emulator or self.guest.emulator 199 self.machines = self.guest.all_machine_names(self.domain) 200 201 202class Capabilities(XMLBuilder): 203 def __init__(self, *args, **kwargs): 204 XMLBuilder.__init__(self, *args, **kwargs) 205 self._cpu_models_cache = {} 206 207 XML_NAME = "capabilities" 208 209 host = XMLChildProperty(_CapsHost, is_single=True) 210 guests = XMLChildProperty(_CapsGuest) 211 212 213 ############################ 214 # Public XML building APIs # 215 ############################ 216 217 def _guestForOSType(self, os_type, arch): 218 archs = [arch] 219 if arch is None: 220 archs = [self.host.cpu.arch, None] 221 222 for a in archs: 223 for g in self.guests: 224 if ((os_type is None or g.os_type == os_type) and 225 (a is None or g.arch == a)): 226 return g 227 228 def _bestDomainType(self, guest, dtype, machine): 229 """ 230 Return the recommended domain for use if the user does not explicitly 231 request one. 232 """ 233 domains = [] 234 for d in guest.domains: 235 if dtype and d.hypervisor_type != dtype.lower(): 236 continue 237 if machine and machine not in guest.all_machine_names(d): 238 continue 239 240 domains.append(d) 241 242 if not domains: 243 return None 244 245 priority = ["kvm", "xen", "qemu"] 246 247 for t in priority: 248 for d in domains: 249 if d.hypervisor_type == t: 250 return d 251 252 # Fallback, just return last item in list 253 return domains[-1] 254 255 def has_install_options(self): 256 """ 257 Return True if there are any install options available 258 """ 259 for guest in self.guests: 260 if guest.domains: 261 return True 262 return False 263 264 def guest_lookup(self, os_type=None, arch=None, typ=None, machine=None): 265 """ 266 Simple virtualization availability lookup 267 268 Convenience function for looking up 'Guest' and 'Domain' capabilities 269 objects for the desired virt type. If type, arch, or os_type are none, 270 we return the default virt type associated with those values. These are 271 typically: 272 273 - os_type : hvm, then xen 274 - typ : kvm over plain qemu 275 - arch : host arch over all others 276 277 Otherwise the default will be the first listed in the capabilities xml. 278 This function throws C{ValueError}s if any of the requested values are 279 not found. 280 281 :param typ: Virtualization type ('hvm', 'xen', ...) 282 :param arch: Guest architecture ('x86_64', 'i686' ...) 283 :param os_type: Hypervisor name ('qemu', 'kvm', 'xen', ...) 284 :param machine: Optional machine type to emulate 285 286 :returns: A _CapsInfo object containing the found guest and domain 287 """ 288 # F22 libxl xen still puts type=linux in the XML, so we need 289 # to handle it for caps lookup 290 if os_type == "linux": 291 os_type = "xen" 292 293 guest = self._guestForOSType(os_type, arch) 294 if not guest: 295 if arch and os_type: 296 msg = (_("Host does not support virtualization type " 297 "'%(virttype)s' for architecture '%(arch)s'") % 298 {'virttype': os_type, 'arch': arch}) 299 elif arch: 300 msg = (_("Host does not support any virtualization options " 301 "for architecture '%(arch)s'") % 302 {'arch': arch}) 303 elif os_type: 304 msg = (_("Host does not support virtualization type " 305 "'%(virttype)s'") % 306 {'virttype': os_type}) 307 else: 308 msg = _("Host does not support any virtualization options") 309 raise ValueError(msg) 310 311 domain = self._bestDomainType(guest, typ, machine) 312 if domain is None: 313 if machine: 314 msg = (_("Host does not support domain type %(domain)s with " 315 "machine '%(machine)s' for virtualization type " 316 "'%(virttype)s' with architecture '%(arch)s'") % 317 {'domain': typ, 'virttype': guest.os_type, 318 'arch': guest.arch, 'machine': machine}) 319 else: 320 msg = (_("Host does not support domain type %(domain)s for " 321 "virtualization type '%(virttype)s' with " 322 "architecture '%(arch)s'") % 323 {'domain': typ, 'virttype': guest.os_type, 324 'arch': guest.arch}) 325 raise ValueError(msg) 326 327 capsinfo = _CapsInfo(self.conn, guest, domain) 328 return capsinfo 329