"Fossies" - the Fresh Open Source Software Archive 
Member "nova-22.0.1/nova/tests/unit/virt/libvirt/fakelibvirt.py" (19 Nov 2020, 64443 Bytes) of package /linux/misc/openstack/nova-22.0.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "fakelibvirt.py":
22.0.0_vs_22.0.1.
1 # Copyright 2010 OpenStack Foundation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14
15 import sys
16 import textwrap
17 import time
18 import typing as ty
19
20 import fixtures
21 from lxml import etree
22 from oslo_log import log as logging
23 from oslo_utils.fixture import uuidsentinel as uuids
24 from oslo_utils import versionutils
25
26 from nova import conf
27 from nova.objects import fields as obj_fields
28 from nova.tests.unit.virt.libvirt import fake_libvirt_data
29 from nova.virt.libvirt import config as vconfig
30 from nova.virt.libvirt import driver as libvirt_driver
31
32
33 # Allow passing None to the various connect methods
34 # (i.e. allow the client to rely on default URLs)
35 allow_default_uri_connection = True
36
37 # Has libvirt connection been used at least once
38 connection_used = False
39
40
41 def _reset():
42 global allow_default_uri_connection
43 allow_default_uri_connection = True
44
45
46 LOG = logging.getLogger(__name__)
47 CONF = conf.CONF
48
49 # virDomainState
50 VIR_DOMAIN_NOSTATE = 0
51 VIR_DOMAIN_RUNNING = 1
52 VIR_DOMAIN_BLOCKED = 2
53 VIR_DOMAIN_PAUSED = 3
54 VIR_DOMAIN_SHUTDOWN = 4
55 VIR_DOMAIN_SHUTOFF = 5
56 VIR_DOMAIN_CRASHED = 6
57
58 # NOTE(mriedem): These values come from include/libvirt/libvirt-domain.h
59 VIR_DOMAIN_XML_SECURE = 1
60 VIR_DOMAIN_XML_INACTIVE = 2
61 VIR_DOMAIN_XML_UPDATE_CPU = 4
62 VIR_DOMAIN_XML_MIGRATABLE = 8
63
64 VIR_DOMAIN_BLOCK_REBASE_SHALLOW = 1
65 VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT = 2
66 VIR_DOMAIN_BLOCK_REBASE_COPY = 8
67 VIR_DOMAIN_BLOCK_REBASE_RELATIVE = 16
68 VIR_DOMAIN_BLOCK_REBASE_COPY_DEV = 32
69
70 # virDomainBlockResize
71 VIR_DOMAIN_BLOCK_RESIZE_BYTES = 1
72
73 VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1
74 VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT = 2
75
76 VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0
77
78 VIR_DOMAIN_EVENT_DEFINED = 0
79 VIR_DOMAIN_EVENT_UNDEFINED = 1
80 VIR_DOMAIN_EVENT_STARTED = 2
81 VIR_DOMAIN_EVENT_SUSPENDED = 3
82 VIR_DOMAIN_EVENT_RESUMED = 4
83 VIR_DOMAIN_EVENT_STOPPED = 5
84 VIR_DOMAIN_EVENT_SHUTDOWN = 6
85 VIR_DOMAIN_EVENT_PMSUSPENDED = 7
86
87 VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED = 1
88 VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY = 7
89
90 VIR_DOMAIN_UNDEFINE_MANAGED_SAVE = 1
91 VIR_DOMAIN_UNDEFINE_NVRAM = 4
92
93 VIR_DOMAIN_AFFECT_CURRENT = 0
94 VIR_DOMAIN_AFFECT_LIVE = 1
95 VIR_DOMAIN_AFFECT_CONFIG = 2
96
97 VIR_CPU_COMPARE_ERROR = -1
98 VIR_CPU_COMPARE_INCOMPATIBLE = 0
99 VIR_CPU_COMPARE_IDENTICAL = 1
100 VIR_CPU_COMPARE_SUPERSET = 2
101
102 VIR_CRED_USERNAME = 1
103 VIR_CRED_AUTHNAME = 2
104 VIR_CRED_LANGUAGE = 3
105 VIR_CRED_CNONCE = 4
106 VIR_CRED_PASSPHRASE = 5
107 VIR_CRED_ECHOPROMPT = 6
108 VIR_CRED_NOECHOPROMPT = 7
109 VIR_CRED_REALM = 8
110 VIR_CRED_EXTERNAL = 9
111
112 VIR_MIGRATE_LIVE = 1
113 VIR_MIGRATE_PEER2PEER = 2
114 VIR_MIGRATE_TUNNELLED = 4
115 VIR_MIGRATE_PERSIST_DEST = 8
116 VIR_MIGRATE_UNDEFINE_SOURCE = 16
117 VIR_MIGRATE_NON_SHARED_INC = 128
118 VIR_MIGRATE_AUTO_CONVERGE = 8192
119 VIR_MIGRATE_POSTCOPY = 32768
120 VIR_MIGRATE_TLS = 65536
121
122 VIR_NODE_CPU_STATS_ALL_CPUS = -1
123
124 VIR_DOMAIN_START_PAUSED = 1
125
126 # libvirtError enums
127 # (Intentionally different from what's in libvirt. We do this to check,
128 # that consumers of the library are using the symbolic names rather than
129 # hardcoding the numerical values)
130 VIR_FROM_QEMU = 100
131 VIR_FROM_DOMAIN = 200
132 VIR_FROM_SECRET = 300
133 VIR_FROM_NWFILTER = 330
134 VIR_FROM_REMOTE = 340
135 VIR_FROM_RPC = 345
136 VIR_FROM_NODEDEV = 666
137
138 VIR_ERR_INVALID_ARG = 8
139 VIR_ERR_NO_SUPPORT = 3
140 VIR_ERR_XML_ERROR = 27
141 VIR_ERR_XML_DETAIL = 350
142 VIR_ERR_NO_DOMAIN = 420
143 VIR_ERR_OPERATION_FAILED = 510
144 VIR_ERR_OPERATION_INVALID = 55
145 VIR_ERR_OPERATION_TIMEOUT = 68
146 VIR_ERR_NO_NWFILTER = 620
147 VIR_ERR_SYSTEM_ERROR = 900
148 VIR_ERR_INTERNAL_ERROR = 950
149 VIR_ERR_CONFIG_UNSUPPORTED = 951
150 VIR_ERR_NO_NODE_DEVICE = 667
151 VIR_ERR_INVALID_SECRET = 65
152 VIR_ERR_NO_SECRET = 66
153 VIR_ERR_AGENT_UNRESPONSIVE = 86
154 VIR_ERR_ARGUMENT_UNSUPPORTED = 74
155 VIR_ERR_OPERATION_UNSUPPORTED = 84
156 VIR_ERR_DEVICE_MISSING = 99
157 # Readonly
158 VIR_CONNECT_RO = 1
159
160 # virConnectBaselineCPU flags
161 VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES = 1
162
163 # snapshotCreateXML flags
164 VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA = 4
165 VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY = 16
166 VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT = 32
167 VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE = 64
168
169 # blockCommit flags
170 VIR_DOMAIN_BLOCK_COMMIT_RELATIVE = 4
171
172
173 VIR_CONNECT_LIST_DOMAINS_ACTIVE = 1
174 VIR_CONNECT_LIST_DOMAINS_INACTIVE = 2
175
176 # virConnectListAllNodeDevices flags
177 VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV = 2
178 VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET = 16
179
180 # secret type
181 VIR_SECRET_USAGE_TYPE_NONE = 0
182 VIR_SECRET_USAGE_TYPE_VOLUME = 1
183 VIR_SECRET_USAGE_TYPE_CEPH = 2
184 VIR_SECRET_USAGE_TYPE_ISCSI = 3
185
186 # Libvirt version to match MIN_LIBVIRT_VERSION in driver.py
187 FAKE_LIBVIRT_VERSION = versionutils.convert_version_to_int(
188 libvirt_driver.MIN_LIBVIRT_VERSION)
189 # Libvirt version to match MIN_QEMU_VERSION in driver.py
190 FAKE_QEMU_VERSION = versionutils.convert_version_to_int(
191 libvirt_driver.MIN_QEMU_VERSION)
192
193 PCI_VEND_ID = '8086'
194 PCI_VEND_NAME = 'Intel Corporation'
195
196 PCI_PROD_ID = '1533'
197 PCI_PROD_NAME = 'I210 Gigabit Network Connection'
198 PCI_DRIVER_NAME = 'igb'
199
200 PF_PROD_ID = '1528'
201 PF_PROD_NAME = 'Ethernet Controller 10-Gigabit X540-AT2'
202 PF_DRIVER_NAME = 'ixgbe'
203 PF_CAP_TYPE = 'virt_functions'
204
205 VF_PROD_ID = '1515'
206 VF_PROD_NAME = 'X540 Ethernet Controller Virtual Function'
207 VF_DRIVER_NAME = 'ixgbevf'
208 VF_CAP_TYPE = 'phys_function'
209
210 MDEV_CAPABLE_VEND_ID = '10DE'
211 MDEV_CAPABLE_VEND_NAME = 'Nvidia'
212 MDEV_CAPABLE_PROD_ID = '0FFE'
213 MDEV_CAPABLE_PROD_NAME = 'GRID M60-0B'
214 MDEV_CAPABLE_DRIVER_NAME = 'nvidia'
215 MDEV_CAPABLE_CAP_TYPE = 'mdev_types'
216
217 NVIDIA_11_VGPU_TYPE = 'nvidia-11'
218 NVIDIA_12_VGPU_TYPE = 'nvidia-12'
219 PGPU1_PCI_ADDR = 'pci_0000_81_00_0'
220 PGPU2_PCI_ADDR = 'pci_0000_81_01_0'
221 PGPU3_PCI_ADDR = 'pci_0000_81_02_0'
222
223
224 class FakePCIDevice(object):
225 """Generate a fake PCI device.
226
227 Generate a fake PCI devices corresponding to one of the following
228 real-world PCI devices.
229
230 - I210 Gigabit Network Connection (8086:1533)
231 - Ethernet Controller 10-Gigabit X540-AT2 (8086:1528)
232 - X540 Ethernet Controller Virtual Function (8086:1515)
233 """
234
235 pci_default_parent = "pci_0000_80_01_0"
236 pci_device_template = textwrap.dedent("""
237 <device>
238 <name>pci_0000_81_%(slot)02x_%(function)d</name>
239 <path>/sys/devices/pci0000:80/0000:80:01.0/0000:81:%(slot)02x.%(function)d</path>
240 <parent>%(parent)s</parent>
241 <driver>
242 <name>%(driver)s</name>
243 </driver>
244 <capability type='pci'>
245 <domain>0</domain>
246 <bus>129</bus>
247 <slot>%(slot)d</slot>
248 <function>%(function)d</function>
249 <product id='0x%(prod_id)s'>%(prod_name)s</product>
250 <vendor id='0x%(vend_id)s'>%(vend_name)s</vendor>
251 %(capability)s
252 <iommuGroup number='%(iommu_group)d'>
253 <address domain='0x0000' bus='0x81' slot='%(slot)#02x' function='0x%(function)d'/>
254 </iommuGroup>
255 <numa node='%(numa_node)s'/>
256 <pci-express>
257 <link validity='cap' port='0' speed='5' width='8'/>
258 <link validity='sta' speed='5' width='8'/>
259 </pci-express>
260 </capability>
261 </device>""".strip()) # noqa
262 cap_templ = "<capability type='%(cap_type)s'>%(addresses)s</capability>"
263 addr_templ = "<address domain='0x0000' bus='0x81' slot='%(slot)#02x' function='%(function)#02x'/>" # noqa
264 mdevtypes_templ = textwrap.dedent("""
265 <type id='%(type_id)s'>
266 <name>GRID M60-0B</name><deviceAPI>vfio-pci</deviceAPI>
267 <availableInstances>%(instances)s</availableInstances>
268 </type>""".strip()) # noqa
269
270 is_capable_of_mdevs = False
271
272 def __init__(self, dev_type, slot, function, iommu_group, numa_node,
273 vf_ratio=None, multiple_gpu_types=False, parent=None):
274 """Populate pci devices
275
276 :param dev_type: (string) Indicates the type of the device (PCI, PF,
277 VF).
278 :param slot: (int) Slot number of the device.
279 :param function: (int) Function number of the device.
280 :param iommu_group: (int) IOMMU group ID.
281 :param numa_node: (int) NUMA node of the device.
282 :param vf_ratio: (int) Ratio of Virtual Functions on Physical. Only
283 applicable if ``dev_type`` is one of: ``PF``, ``VF``.
284 :param multiple_gpu_types: (bool) Supports different vGPU types
285 """
286
287 self.dev_type = dev_type
288 self.slot = slot
289 self.function = function
290 self.iommu_group = iommu_group
291 self.numa_node = numa_node
292 self.vf_ratio = vf_ratio
293 self.multiple_gpu_types = multiple_gpu_types
294 self.parent = parent
295 self.generate_xml()
296
297 def generate_xml(self, skip_capability=False):
298 vend_id = PCI_VEND_ID
299 vend_name = PCI_VEND_NAME
300 capability = ''
301 if self.dev_type == 'PCI':
302 if self.vf_ratio:
303 raise ValueError('vf_ratio does not apply for PCI devices')
304
305 prod_id = PCI_PROD_ID
306 prod_name = PCI_PROD_NAME
307 driver = PCI_DRIVER_NAME
308 elif self.dev_type == 'PF':
309 prod_id = PF_PROD_ID
310 prod_name = PF_PROD_NAME
311 driver = PF_DRIVER_NAME
312 if not skip_capability:
313 capability = self.cap_templ % {
314 'cap_type': PF_CAP_TYPE,
315 'addresses': '\n'.join([
316 self.addr_templ % {
317 # these are the slot, function values of the child
318 # VFs, we can only assign 8 functions to a slot
319 # (0-7) so bump the slot each time we exceed this
320 'slot': self.slot + (x // 8),
321 # ...and wrap the function value
322 'function': x % 8,
323 # the offset is because the PF is occupying function 0
324 } for x in range(1, self.vf_ratio + 1)])
325 }
326 elif self.dev_type == 'VF':
327 prod_id = VF_PROD_ID
328 prod_name = VF_PROD_NAME
329 driver = VF_DRIVER_NAME
330 if not skip_capability:
331 capability = self.cap_templ % {
332 'cap_type': VF_CAP_TYPE,
333 'addresses': self.addr_templ % {
334 # this is the slot, function value of the parent PF
335 # if we're e.g. device 8, we'll have a different slot
336 # to our parent so reverse this
337 'slot': self.slot - ((self.vf_ratio + 1) // 8),
338 # the parent PF is always function 0
339 'function': 0,
340 }
341 }
342 elif self.dev_type == 'MDEV_TYPES':
343 prod_id = MDEV_CAPABLE_PROD_ID
344 prod_name = MDEV_CAPABLE_PROD_NAME
345 driver = MDEV_CAPABLE_DRIVER_NAME
346 vend_id = MDEV_CAPABLE_VEND_ID
347 vend_name = MDEV_CAPABLE_VEND_NAME
348 types = [self.mdevtypes_templ % {
349 'type_id': NVIDIA_11_VGPU_TYPE,
350 'instances': 16,
351 }]
352 if self.multiple_gpu_types:
353 types.append(self.mdevtypes_templ % {
354 'type_id': NVIDIA_12_VGPU_TYPE,
355 'instances': 8,
356 })
357 if not skip_capability:
358 capability = self.cap_templ % {
359 'cap_type': MDEV_CAPABLE_CAP_TYPE,
360 'addresses': '\n'.join(types)
361 }
362 self.is_capable_of_mdevs = True
363 else:
364 raise ValueError('Expected one of: PCI, VF, PCI')
365
366 self.pci_device = self.pci_device_template % {
367 'slot': self.slot,
368 'function': self.function,
369 'vend_id': vend_id,
370 'vend_name': vend_name,
371 'prod_id': prod_id,
372 'prod_name': prod_name,
373 'driver': driver,
374 'capability': capability,
375 'iommu_group': self.iommu_group,
376 'numa_node': self.numa_node,
377 'parent': self.parent or self.pci_default_parent
378 }
379 # -1 is the sentinel set in /sys/bus/pci/devices/*/numa_node
380 # for no NUMA affinity. When the numa_node is set to -1 on a device
381 # Libvirt omits the NUMA element so we remove it.
382 if self.numa_node == -1:
383 self.pci_device = self.pci_device.replace("<numa node='-1'/>", "")
384
385 def XMLDesc(self, flags):
386 return self.pci_device
387
388
389 class HostPCIDevicesInfo(object):
390 """Represent a pool of host PCI devices."""
391
392 TOTAL_NUMA_NODES = 2
393 pci_devname_template = 'pci_0000_81_%(slot)02x_%(function)d'
394
395 def __init__(self, num_pci=0, num_pfs=2, num_vfs=8, num_mdevcap=0,
396 numa_node=None, multiple_gpu_types=False):
397 """Create a new HostPCIDevicesInfo object.
398
399 :param num_pci: (int) The number of (non-SR-IOV) and (non-MDEV capable)
400 PCI devices.
401 :param num_pfs: (int) The number of PCI SR-IOV Physical Functions.
402 :param num_vfs: (int) The number of PCI SR-IOV Virtual Functions.
403 :param num_mdevcap: (int) The number of PCI devices capable of creating
404 mediated devices.
405 :param iommu_group: (int) Initial IOMMU group ID.
406 :param numa_node: (int) NUMA node of the device; if set all of the
407 devices will be assigned to the specified node else they will be
408 split between ``$TOTAL_NUMA_NODES`` nodes.
409 :param multiple_gpu_types: (bool) Supports different vGPU types
410 """
411 self.devices = {}
412
413 if not (num_vfs or num_pfs) and not num_mdevcap:
414 return
415
416 if num_vfs and not num_pfs:
417 raise ValueError('Cannot create VFs without PFs')
418
419 if num_pfs and num_vfs % num_pfs:
420 raise ValueError('num_vfs must be a factor of num_pfs')
421
422 slot = 0
423 function = 0
424 iommu_group = 40 # totally arbitrary number
425
426 # Generate PCI devs
427 for dev in range(num_pci):
428 pci_dev_name = self.pci_devname_template % {
429 'slot': slot, 'function': function}
430
431 LOG.info('Generating PCI device %r', pci_dev_name)
432
433 self.devices[pci_dev_name] = FakePCIDevice(
434 dev_type='PCI',
435 slot=slot,
436 function=function,
437 iommu_group=iommu_group,
438 numa_node=self._calc_numa_node(dev, numa_node))
439
440 slot += 1
441 iommu_group += 1
442
443 # Generate MDEV capable devs
444 for dev in range(num_mdevcap):
445 pci_dev_name = self.pci_devname_template % {
446 'slot': slot, 'function': function}
447
448 LOG.info('Generating MDEV capable device %r', pci_dev_name)
449
450 self.devices[pci_dev_name] = FakePCIDevice(
451 dev_type='MDEV_TYPES',
452 slot=slot,
453 function=function,
454 iommu_group=iommu_group,
455 numa_node=self._calc_numa_node(dev, numa_node),
456 multiple_gpu_types=multiple_gpu_types)
457
458 slot += 1
459 iommu_group += 1
460
461 vf_ratio = num_vfs // num_pfs if num_pfs else 0
462
463 # Generate PFs
464 for dev in range(num_pfs):
465 function = 0
466 numa_node_pf = self._calc_numa_node(dev, numa_node)
467
468 pci_dev_name = self.pci_devname_template % {
469 'slot': slot, 'function': function}
470
471 LOG.info('Generating PF device %r', pci_dev_name)
472
473 self.devices[pci_dev_name] = FakePCIDevice(
474 dev_type='PF',
475 slot=slot,
476 function=function,
477 iommu_group=iommu_group,
478 numa_node=numa_node_pf,
479 vf_ratio=vf_ratio)
480 pf_dev_name = pci_dev_name
481 # Generate VFs
482 for _ in range(vf_ratio):
483 function += 1
484 iommu_group += 1
485
486 if function % 8 == 0:
487 # functions must be 0-7
488 slot += 1
489 function = 0
490
491 pci_dev_name = self.pci_devname_template % {
492 'slot': slot, 'function': function}
493
494 LOG.info('Generating VF device %r', pci_dev_name)
495
496 self.devices[pci_dev_name] = FakePCIDevice(
497 dev_type='VF',
498 slot=slot,
499 function=function,
500 iommu_group=iommu_group,
501 numa_node=numa_node_pf,
502 vf_ratio=vf_ratio,
503 parent=pf_dev_name)
504
505 slot += 1
506
507 @classmethod
508 def _calc_numa_node(cls, dev, numa_node):
509 return dev % cls.TOTAL_NUMA_NODES if numa_node is None else numa_node
510
511 def get_all_devices(self):
512 return self.devices.keys()
513
514 def get_device_by_name(self, device_name):
515 pci_dev = self.devices.get(device_name)
516 return pci_dev
517
518 def get_all_mdev_capable_devices(self):
519 return [dev for dev in self.devices
520 if self.devices[dev].is_capable_of_mdevs]
521
522
523 class FakeMdevDevice(object):
524 template = """
525 <device>
526 <name>%(dev_name)s</name>
527 <path>/sys/devices/pci0000:00/0000:00:02.0/%(path)s</path>
528 <parent>%(parent)s</parent>
529 <driver>
530 <name>vfio_mdev</name>
531 </driver>
532 <capability type='mdev'>
533 <type id='%(type_id)s'/>
534 <iommuGroup number='12'/>
535 </capability>
536 </device>
537 """
538
539 def __init__(self, dev_name, type_id, parent):
540 self.xml = self.template % {
541 'dev_name': dev_name, 'type_id': type_id,
542 'path': dev_name[len('mdev_'):],
543 'parent': parent}
544
545 def XMLDesc(self, flags):
546 return self.xml
547
548
549 class HostMdevDevicesInfo(object):
550 def __init__(self, devices=None):
551 if devices is not None:
552 self.devices = devices
553 else:
554 self.devices = {}
555
556 def get_all_devices(self):
557 return self.devices.keys()
558
559 def get_device_by_name(self, device_name):
560 dev = self.devices[device_name]
561 return dev
562
563
564 class HostInfo(object):
565
566 def __init__(self, cpu_nodes=1, cpu_sockets=1, cpu_cores=2, cpu_threads=1,
567 kB_mem=4096, mempages=None):
568 """Create a new Host Info object
569
570 :param cpu_nodes: (int) the number of NUMA cell, 1 for unusual
571 NUMA topologies or uniform
572 :param cpu_sockets: (int) number of CPU sockets per node if nodes > 1,
573 total number of CPU sockets otherwise
574 :param cpu_cores: (int) number of cores per socket
575 :param cpu_threads: (int) number of threads per core
576 :param kB_mem: (int) memory size in KBytes
577 """
578
579 self.arch = obj_fields.Architecture.X86_64
580 self.kB_mem = kB_mem
581 self.cpus = cpu_nodes * cpu_sockets * cpu_cores * cpu_threads
582 self.cpu_mhz = 800
583 self.cpu_nodes = cpu_nodes
584 self.cpu_cores = cpu_cores
585 self.cpu_threads = cpu_threads
586 self.cpu_sockets = cpu_sockets
587 self.cpu_model = "Penryn"
588 self.cpu_vendor = "Intel"
589 self.numa_topology = NUMATopology(self.cpu_nodes, self.cpu_sockets,
590 self.cpu_cores, self.cpu_threads,
591 self.kB_mem, mempages)
592
593
594 class NUMATopology(vconfig.LibvirtConfigCapsNUMATopology):
595 """A batteries-included variant of LibvirtConfigCapsNUMATopology.
596
597 Provides sane defaults for LibvirtConfigCapsNUMATopology that can be used
598 in tests as is, or overridden where necessary.
599 """
600
601 def __init__(self, cpu_nodes=4, cpu_sockets=1, cpu_cores=1, cpu_threads=2,
602 kb_mem=1048576, mempages=None, **kwargs):
603
604 super(NUMATopology, self).__init__(**kwargs)
605
606 cpu_count = 0
607 for cell_count in range(cpu_nodes):
608 cell = vconfig.LibvirtConfigCapsNUMACell()
609 cell.id = cell_count
610 cell.memory = kb_mem // cpu_nodes
611 for socket_count in range(cpu_sockets):
612 for cpu_num in range(cpu_cores * cpu_threads):
613 cpu = vconfig.LibvirtConfigCapsNUMACPU()
614 cpu.id = cpu_count
615 cpu.socket_id = cell_count
616 cpu.core_id = cpu_num // cpu_threads
617 cpu.siblings = set([cpu_threads *
618 (cpu_count // cpu_threads) + thread
619 for thread in range(cpu_threads)])
620 cell.cpus.append(cpu)
621
622 cpu_count += 1
623
624 # If no mempages are provided, use only the default 4K pages
625 if mempages:
626 cell.mempages = mempages[cell_count]
627 else:
628 cell.mempages = create_mempages([(4, cell.memory // 4)])
629
630 self.cells.append(cell)
631
632
633 def create_mempages(mappings):
634 """Generate a list of LibvirtConfigCapsNUMAPages objects.
635
636 :param mappings: (dict) A mapping of page size to quantity of
637 said pages.
638 :returns: [LibvirtConfigCapsNUMAPages, ...]
639 """
640 mempages = []
641
642 for page_size, page_qty in mappings:
643 mempage = vconfig.LibvirtConfigCapsNUMAPages()
644 mempage.size = page_size
645 mempage.total = page_qty
646 mempages.append(mempage)
647
648 return mempages
649
650
651 VIR_DOMAIN_JOB_NONE = 0
652 VIR_DOMAIN_JOB_BOUNDED = 1
653 VIR_DOMAIN_JOB_UNBOUNDED = 2
654 VIR_DOMAIN_JOB_COMPLETED = 3
655 VIR_DOMAIN_JOB_FAILED = 4
656 VIR_DOMAIN_JOB_CANCELLED = 5
657
658
659 def _parse_disk_info(element):
660 disk_info = {}
661 disk_info['type'] = element.get('type', 'file')
662 disk_info['device'] = element.get('device', 'disk')
663
664 driver = element.find('./driver')
665 if driver is not None:
666 disk_info['driver_name'] = driver.get('name')
667 disk_info['driver_type'] = driver.get('type')
668
669 source = element.find('./source')
670 if source is not None:
671 disk_info['source'] = source.get('file')
672 if not disk_info['source']:
673 disk_info['source'] = source.get('dev')
674
675 if not disk_info['source']:
676 disk_info['source'] = source.get('path')
677
678 target = element.find('./target')
679 if target is not None:
680 disk_info['target_dev'] = target.get('dev')
681 disk_info['target_bus'] = target.get('bus')
682
683 return disk_info
684
685
686 def _parse_nic_info(element):
687 nic_info = {}
688 nic_info['type'] = element.get('type', 'bridge')
689
690 driver = element.find('./mac')
691 if driver is not None:
692 nic_info['mac'] = driver.get('address')
693
694 source = element.find('./source')
695 if source is not None:
696 nic_info['source'] = source.get('bridge')
697
698 target = element.find('./target')
699 if target is not None:
700 nic_info['target_dev'] = target.get('dev')
701
702 return nic_info
703
704
705 def disable_event_thread(self):
706 """Disable nova libvirt driver event thread.
707
708 The Nova libvirt driver includes a native thread which monitors
709 the libvirt event channel. In a testing environment this becomes
710 problematic because it means we've got a floating thread calling
711 sleep(1) over the life of the unit test. Seems harmless? It's not,
712 because we sometimes want to test things like retry loops that
713 should have specific sleep paterns. An unlucky firing of the
714 libvirt thread will cause a test failure.
715
716 """
717 # because we are patching a method in a class MonkeyPatch doesn't
718 # auto import correctly. Import explicitly otherwise the patching
719 # may silently fail.
720 import nova.virt.libvirt.host # noqa
721
722 def evloop(*args, **kwargs):
723 pass
724
725 self.useFixture(fixtures.MockPatch(
726 'nova.virt.libvirt.host.Host._init_events',
727 side_effect=evloop))
728
729
730 class libvirtError(Exception):
731 """This class was copied and slightly modified from
732 `libvirt-python:libvirt-override.py`.
733
734 Since a test environment will use the real `libvirt-python` version of
735 `libvirtError` if it's installed and not this fake, we need to maintain
736 strict compatibility with the original class, including `__init__` args
737 and instance-attributes.
738
739 To create a libvirtError instance you should:
740
741 # Create an unsupported error exception
742 exc = libvirtError('my message')
743 exc.err = (libvirt.VIR_ERR_NO_SUPPORT,)
744
745 self.err is a tuple of form:
746 (error_code, error_domain, error_message, error_level, str1, str2,
747 str3, int1, int2)
748
749 Alternatively, you can use the `make_libvirtError` convenience function to
750 allow you to specify these attributes in one shot.
751 """
752 def __init__(self, defmsg, conn=None, dom=None, net=None, pool=None,
753 vol=None):
754 Exception.__init__(self, defmsg)
755 self.err = None
756
757 def get_error_code(self):
758 if self.err is None:
759 return None
760 return self.err[0]
761
762 def get_error_domain(self):
763 if self.err is None:
764 return None
765 return self.err[1]
766
767 def get_error_message(self):
768 if self.err is None:
769 return None
770 return self.err[2]
771
772 def get_error_level(self):
773 if self.err is None:
774 return None
775 return self.err[3]
776
777 def get_str1(self):
778 if self.err is None:
779 return None
780 return self.err[4]
781
782 def get_str2(self):
783 if self.err is None:
784 return None
785 return self.err[5]
786
787 def get_str3(self):
788 if self.err is None:
789 return None
790 return self.err[6]
791
792 def get_int1(self):
793 if self.err is None:
794 return None
795 return self.err[7]
796
797 def get_int2(self):
798 if self.err is None:
799 return None
800 return self.err[8]
801
802
803 class NodeDevice(object):
804
805 def __init__(self, connection, xml=None):
806 self._connection = connection
807
808 self._xml = xml
809 if xml is not None:
810 self._parse_xml(xml)
811
812 def _parse_xml(self, xml):
813 tree = etree.fromstring(xml)
814 root = tree.find('.')
815 self._name = root.find('name').text
816 self._parent = root.find('parent').text
817
818 def attach(self):
819 pass
820
821 def dettach(self):
822 pass
823
824 def reset(self):
825 pass
826
827 def XMLDesc(self, flags: int) -> str:
828 return self._xml
829
830 def parent(self) -> str:
831 return self._parent
832
833 def name(self) -> str:
834 return self._name
835
836 def listCaps(self) -> ty.List[str]:
837 return [self.name().split('_')[0]]
838
839
840 class Domain(object):
841 def __init__(self, connection, xml, running=False, transient=False):
842 self._connection = connection
843 if running:
844 connection._mark_running(self)
845
846 self._state = running and VIR_DOMAIN_RUNNING or VIR_DOMAIN_SHUTOFF
847 self._transient = transient
848 self._def = self._parse_definition(xml)
849 self._has_saved_state = False
850 self._snapshots = {}
851 self._id = self._connection._id_counter
852 self._job_type = VIR_DOMAIN_JOB_UNBOUNDED
853
854 def _parse_definition(self, xml):
855 try:
856 tree = etree.fromstring(xml)
857 except etree.ParseError:
858 raise make_libvirtError(
859 libvirtError, "Invalid XML.",
860 error_code=VIR_ERR_XML_DETAIL,
861 error_domain=VIR_FROM_DOMAIN)
862
863 definition = {}
864
865 name = tree.find('./name')
866 if name is not None:
867 definition['name'] = name.text
868
869 uuid_elem = tree.find('./uuid')
870 if uuid_elem is not None:
871 definition['uuid'] = uuid_elem.text
872 else:
873 definition['uuid'] = uuids.fake
874
875 vcpu = tree.find('./vcpu')
876 if vcpu is not None:
877 definition['vcpu'] = int(vcpu.text)
878
879 memory = tree.find('./memory')
880 if memory is not None:
881 definition['memory'] = int(memory.text)
882
883 os = {}
884 os_type = tree.find('./os/type')
885 if os_type is not None:
886 os['type'] = os_type.text
887 os['arch'] = os_type.get('arch', self._connection.host_info.arch)
888
889 os_kernel = tree.find('./os/kernel')
890 if os_kernel is not None:
891 os['kernel'] = os_kernel.text
892
893 os_initrd = tree.find('./os/initrd')
894 if os_initrd is not None:
895 os['initrd'] = os_initrd.text
896
897 os_cmdline = tree.find('./os/cmdline')
898 if os_cmdline is not None:
899 os['cmdline'] = os_cmdline.text
900
901 os_boot = tree.find('./os/boot')
902 if os_boot is not None:
903 os['boot_dev'] = os_boot.get('dev')
904
905 definition['os'] = os
906
907 features = {}
908
909 acpi = tree.find('./features/acpi')
910 if acpi is not None:
911 features['acpi'] = True
912
913 definition['features'] = features
914
915 cpu_pins = {}
916
917 pins = tree.findall('./cputune/vcpupin')
918 for pin in pins:
919 cpu_pins[pin.get('vcpu')] = pin.get('cpuset')
920
921 definition['cpu_pins'] = cpu_pins
922
923 emulator_pin = tree.find('./cputune/emulatorpin')
924 if emulator_pin is not None:
925 definition['emulator_pin'] = emulator_pin.get('cpuset')
926
927 memnodes = {}
928
929 for node in tree.findall('./numatune/memnode'):
930 memnodes[node.get('cellid')] = node.get('nodeset')
931
932 definition['memnodes'] = memnodes
933
934 devices = {}
935
936 device_nodes = tree.find('./devices')
937 if device_nodes is not None:
938 disks_info = []
939 disks = device_nodes.findall('./disk')
940 for disk in disks:
941 disks_info += [_parse_disk_info(disk)]
942 devices['disks'] = disks_info
943
944 nics_info = []
945 nics = device_nodes.findall('./interface')
946 for nic in nics:
947 nic_info = {}
948 nic_info['type'] = nic.get('type')
949
950 mac = nic.find('./mac')
951 if mac is not None:
952 nic_info['mac'] = mac.get('address')
953
954 source = nic.find('./source')
955 if source is not None:
956 if nic_info['type'] == 'network':
957 nic_info['source'] = source.get('network')
958 elif nic_info['type'] == 'bridge':
959 nic_info['source'] = source.get('bridge')
960 elif nic_info['type'] == 'hostdev':
961 # <interface type='hostdev'> is for VF when vnic_type
962 # is direct. Add sriov vf pci information in nic_info
963 address = source.find('./address')
964 pci_type = address.get('type')
965 pci_domain = address.get('domain').replace('0x', '')
966 pci_bus = address.get('bus').replace('0x', '')
967 pci_slot = address.get('slot').replace('0x', '')
968 pci_function = address.get('function').replace(
969 '0x', '')
970 pci_device = "%s_%s_%s_%s_%s" % (pci_type, pci_domain,
971 pci_bus, pci_slot,
972 pci_function)
973 nic_info['source'] = pci_device
974
975 nics_info += [nic_info]
976
977 devices['nics'] = nics_info
978
979 hostdev_info = []
980 hostdevs = device_nodes.findall('./hostdev')
981 for hostdev in hostdevs:
982 address = hostdev.find('./source/address')
983 # NOTE(gibi): only handle mdevs as pci is complicated
984 dev_type = hostdev.get('type')
985 if dev_type == 'mdev':
986 hostdev_info.append({
987 'type': dev_type,
988 'model': hostdev.get('model'),
989 'address_uuid': address.get('uuid')
990 })
991 devices['hostdevs'] = hostdev_info
992
993 vpmem_info = []
994 vpmems = device_nodes.findall('./memory')
995 for vpmem in vpmems:
996 model = vpmem.get('model')
997 if model == 'nvdimm':
998 source = vpmem.find('./source')
999 target = vpmem.find('./target')
1000 path = source.find('./path').text
1001 alignsize = source.find('./alignsize').text
1002 size = target.find('./size').text
1003 node = target.find('./node').text
1004 vpmem_info.append({
1005 'path': path,
1006 'size': size,
1007 'alignsize': alignsize,
1008 'node': node})
1009 devices['vpmems'] = vpmem_info
1010
1011 definition['devices'] = devices
1012
1013 return definition
1014
1015 def verify_hostdevs_interface_are_vfs(self):
1016 """Verify for interface type hostdev if the pci device is VF or not.
1017 """
1018
1019 error_message = ("Interface type hostdev is currently supported on "
1020 "SR-IOV Virtual Functions only")
1021
1022 nics = self._def['devices'].get('nics', [])
1023 for nic in nics:
1024 if nic['type'] == 'hostdev':
1025 pci_device = nic['source']
1026 pci_info_from_connection = self._connection.pci_info.devices[
1027 pci_device]
1028 if 'phys_function' not in pci_info_from_connection.pci_device:
1029 raise make_libvirtError(
1030 libvirtError,
1031 error_message,
1032 error_code=VIR_ERR_CONFIG_UNSUPPORTED,
1033 error_domain=VIR_FROM_DOMAIN)
1034
1035 def create(self):
1036 self.createWithFlags(0)
1037
1038 def createWithFlags(self, flags):
1039 # FIXME: Not handling flags at the moment
1040 self.verify_hostdevs_interface_are_vfs()
1041 self._state = VIR_DOMAIN_RUNNING
1042 self._connection._mark_running(self)
1043 self._has_saved_state = False
1044
1045 def isActive(self):
1046 return int(self._state == VIR_DOMAIN_RUNNING)
1047
1048 def undefine(self):
1049 self._connection._undefine(self)
1050
1051 def isPersistent(self):
1052 return True
1053
1054 def undefineFlags(self, flags):
1055 self.undefine()
1056 if flags & VIR_DOMAIN_UNDEFINE_MANAGED_SAVE:
1057 if self.hasManagedSaveImage(0):
1058 self.managedSaveRemove()
1059
1060 def destroy(self):
1061 self._state = VIR_DOMAIN_SHUTOFF
1062 self._connection._mark_not_running(self)
1063
1064 def ID(self):
1065 return self._id
1066
1067 def name(self):
1068 return self._def['name']
1069
1070 def UUIDString(self):
1071 return self._def['uuid']
1072
1073 def interfaceStats(self, device):
1074 return [10000242400, 1234, 0, 2, 213412343233, 34214234, 23, 3]
1075
1076 def blockStats(self, device):
1077 return [2, 10000242400, 234, 2343424234, 34]
1078
1079 def setTime(self, time=None, flags=0):
1080 pass
1081
1082 def suspend(self):
1083 self._state = VIR_DOMAIN_PAUSED
1084
1085 def shutdown(self):
1086 self._state = VIR_DOMAIN_SHUTDOWN
1087 self._connection._mark_not_running(self)
1088
1089 def reset(self, flags):
1090 # FIXME: Not handling flags at the moment
1091 self._state = VIR_DOMAIN_RUNNING
1092 self._connection._mark_running(self)
1093
1094 def info(self):
1095 return [self._state,
1096 int(self._def['memory']),
1097 int(self._def['memory']),
1098 self._def['vcpu'],
1099 123456789]
1100
1101 def migrateToURI3(self, dconnuri, params, flags):
1102 raise make_libvirtError(
1103 libvirtError,
1104 "Migration always fails for fake libvirt!",
1105 error_code=VIR_ERR_INTERNAL_ERROR,
1106 error_domain=VIR_FROM_QEMU)
1107
1108 def migrateSetMaxDowntime(self, downtime):
1109 pass
1110
1111 def attachDevice(self, xml):
1112 result = False
1113 if xml.startswith("<disk"):
1114 disk_info = _parse_disk_info(etree.fromstring(xml))
1115 disk_info['_attached'] = True
1116 self._def['devices']['disks'] += [disk_info]
1117 result = True
1118 elif xml.startswith("<interface"):
1119 nic_info = _parse_nic_info(etree.fromstring(xml))
1120 nic_info['_attached'] = True
1121 self._def['devices']['nics'] += [nic_info]
1122 result = True
1123
1124 return result
1125
1126 def attachDeviceFlags(self, xml, flags):
1127 if (flags & VIR_DOMAIN_AFFECT_LIVE and
1128 self._state != VIR_DOMAIN_RUNNING):
1129 raise make_libvirtError(
1130 libvirtError,
1131 "AFFECT_LIVE only allowed for running domains!",
1132 error_code=VIR_ERR_INTERNAL_ERROR,
1133 error_domain=VIR_FROM_QEMU)
1134 self.attachDevice(xml)
1135
1136 def detachDevice(self, xml):
1137 disk_info = _parse_disk_info(etree.fromstring(xml))
1138 disk_info['_attached'] = True
1139 return disk_info in self._def['devices']['disks']
1140
1141 def detachDeviceFlags(self, xml, flags):
1142 self.detachDevice(xml)
1143
1144 def setUserPassword(self, user, password, flags=0):
1145 pass
1146
1147 def XMLDesc(self, flags):
1148 disks = ''
1149 for disk in self._def['devices']['disks']:
1150 if disk['type'] == 'file':
1151 source_attr = 'file'
1152 else:
1153 source_attr = 'dev'
1154
1155 disks += '''<disk type='%(type)s' device='%(device)s'>
1156 <driver name='%(driver_name)s' type='%(driver_type)s'/>
1157 <source %(source_attr)s='%(source)s'/>
1158 <target dev='%(target_dev)s' bus='%(target_bus)s'/>
1159 <address type='drive' controller='0' bus='0' unit='0'/>
1160 </disk>''' % dict(source_attr=source_attr, **disk)
1161
1162 nics = ''
1163 for nic in self._def['devices']['nics']:
1164 if 'source' in nic and nic['type'] != 'hostdev':
1165 nics += '''<interface type='%(type)s'>
1166 <mac address='%(mac)s'/>
1167 <source %(type)s='%(source)s'/>
1168 <target dev='tap274487d1-60'/>
1169 <address type='pci' domain='0x0000' bus='0x00' slot='0x03'
1170 function='0x0'/>
1171 </interface>''' % nic
1172 # this covers for direct nic type
1173 else:
1174 nics += '''<interface type='%(type)s'>
1175 <mac address='%(mac)s'/>
1176 <source>
1177 <address type='pci' domain='0x0000' bus='0x81' slot='0x00'
1178 function='0x01'/>
1179 </source>
1180 </interface>''' % nic
1181
1182 hostdevs = ''
1183 for hostdev in self._def['devices']['hostdevs']:
1184 hostdevs += '''<hostdev mode='subsystem' type='%(type)s' model='%(model)s'>
1185 <source>
1186 <address uuid='%(address_uuid)s'/>
1187 </source>
1188 </hostdev>
1189 ''' % hostdev # noqa
1190
1191 vpmems = ''
1192 for vpmem in self._def['devices']['vpmems']:
1193 vpmems += '''
1194 <memory model='nvdimm' access='shared'>
1195 <source>
1196 <path>%(path)s</path>
1197 <alignsize>%(alignsize)s</alignsize>
1198 <pmem/>
1199 </source>
1200 <target>
1201 <size>%(size)s</size>
1202 <node>%(node)s</node>
1203 <label>
1204 <size>2097152</size>
1205 </label>
1206 </target>
1207 </memory>
1208 ''' % vpmem
1209 cputune = ''
1210 for vcpu, cpuset in self._def['cpu_pins'].items():
1211 cputune += '<vcpupin vcpu="%d" cpuset="%s"/>' % (int(vcpu), cpuset)
1212 emulatorpin = None
1213 if 'emulator_pin' in self._def:
1214 emulatorpin = ('<emulatorpin cpuset="%s"/>' %
1215 self._def['emulator_pin'])
1216 if cputune or emulatorpin:
1217 cputune = '<cputune>%s%s</cputune>' % (emulatorpin, cputune)
1218
1219 numatune = ''
1220 for cellid, nodeset in self._def['memnodes'].items():
1221 numatune += '<memnode cellid="%d" nodeset="%s"/>' % (int(cellid),
1222 nodeset)
1223 numatune += '<memory nodeset="%s"/>' % ','.join(
1224 self._def['memnodes'].values())
1225 if numatune:
1226 numatune = '<numatune>%s</numatune>' % numatune
1227
1228 serial_console = ''
1229 if CONF.serial_console.enabled:
1230 serial_console = """<serial type="tcp">
1231 <source host="-1" service="-1" mode="bind"/>
1232 </serial>"""
1233
1234 return '''<domain type='kvm'>
1235 <name>%(name)s</name>
1236 <uuid>%(uuid)s</uuid>
1237 <memory>%(memory)s</memory>
1238 <currentMemory>%(memory)s</currentMemory>
1239 <vcpu>%(vcpu)s</vcpu>
1240 <os>
1241 <type arch='%(arch)s' machine='pc-0.12'>hvm</type>
1242 <boot dev='hd'/>
1243 </os>
1244 <features>
1245 <acpi/>
1246 <apic/>
1247 <pae/>
1248 </features>
1249 <clock offset='localtime'/>
1250 <on_poweroff>destroy</on_poweroff>
1251 <on_reboot>restart</on_reboot>
1252 <on_crash>restart</on_crash>
1253 %(cputune)s
1254 %(numatune)s
1255 <devices>
1256 <emulator>/usr/bin/kvm</emulator>
1257 %(disks)s
1258 <controller type='ide' index='0'>
1259 <address type='pci' domain='0x0000' bus='0x00' slot='0x01'
1260 function='0x1'/>
1261 </controller>
1262 %(nics)s
1263 %(serial_console)s
1264 <console type='file'>
1265 <source path='dummy.log'/>
1266 <target port='0'/>
1267 </console>
1268 <input type='tablet' bus='usb'/>
1269 <input type='mouse' bus='ps2'/>
1270 <graphics type='vnc' port='-1' autoport='yes'/>
1271 <graphics type='spice' port='-1' autoport='yes'/>
1272 <video>
1273 <model type='cirrus' vram='9216' heads='1'/>
1274 <address type='pci' domain='0x0000' bus='0x00' slot='0x02'
1275 function='0x0'/>
1276 </video>
1277 <memballoon model='virtio'>
1278 <address type='pci' domain='0x0000' bus='0x00' slot='0x04'
1279 function='0x0'/>
1280 </memballoon>
1281 %(hostdevs)s
1282 %(vpmems)s
1283 </devices>
1284 </domain>''' % {'name': self._def['name'],
1285 'uuid': self._def['uuid'],
1286 'memory': self._def['memory'],
1287 'vcpu': self._def['vcpu'],
1288 'arch': self._def['os']['arch'],
1289 'disks': disks,
1290 'nics': nics,
1291 'hostdevs': hostdevs,
1292 'vpmems': vpmems,
1293 'serial_console': serial_console,
1294 'cputune': cputune,
1295 'numatune': numatune}
1296
1297 def managedSave(self, flags):
1298 self._connection._mark_not_running(self)
1299 self._has_saved_state = True
1300
1301 def managedSaveRemove(self, flags):
1302 self._has_saved_state = False
1303
1304 def hasManagedSaveImage(self, flags):
1305 return int(self._has_saved_state)
1306
1307 def resume(self):
1308 self._state = VIR_DOMAIN_RUNNING
1309
1310 def snapshotCreateXML(self, xml, flags):
1311 tree = etree.fromstring(xml)
1312 name = tree.find('./name').text
1313 snapshot = DomainSnapshot(name, self)
1314 self._snapshots[name] = snapshot
1315 return snapshot
1316
1317 def vcpus(self):
1318 vcpus = ([], [])
1319 for i in range(0, self._def['vcpu']):
1320 vcpus[0].append((i, 1, 120405, i))
1321 vcpus[1].append((True, True, True, True))
1322 return vcpus
1323
1324 def memoryStats(self):
1325 return {}
1326
1327 def maxMemory(self):
1328 return self._def['memory']
1329
1330 def blockJobInfo(self, disk, flags):
1331 return {}
1332
1333 def blockJobAbort(self, disk, flags):
1334 pass
1335
1336 def blockResize(self, disk, size, flags):
1337 pass
1338
1339 def blockRebase(self, disk, base, bandwidth=0, flags=0):
1340 if (not base) and (flags and VIR_DOMAIN_BLOCK_REBASE_RELATIVE):
1341 raise make_libvirtError(
1342 libvirtError,
1343 'flag VIR_DOMAIN_BLOCK_REBASE_RELATIVE is '
1344 'valid only with non-null base',
1345 error_code=VIR_ERR_INVALID_ARG,
1346 error_domain=VIR_FROM_QEMU)
1347 return 0
1348
1349 def blockCommit(self, disk, base, top, flags):
1350 return 0
1351
1352 def jobInfo(self):
1353 # NOTE(danms): This is an array of 12 integers, so just report
1354 # something to avoid an IndexError if we look at this
1355 return [0] * 12
1356
1357 def jobStats(self, flags=0):
1358 # NOTE(artom) By returning VIR_DOMAIN_JOB_UNBOUNDED, we're pretending a
1359 # job is constantly running. Tests are expected to call the
1360 # complete_job or fail_job methods when they're ready for jobs (read:
1361 # live migrations) to "complete".
1362 return {'type': self._job_type}
1363
1364 def complete_job(self):
1365 self._job_type = VIR_DOMAIN_JOB_COMPLETED
1366
1367 def fail_job(self):
1368 self._job_type = VIR_DOMAIN_JOB_FAILED
1369
1370 def injectNMI(self, flags=0):
1371 return 0
1372
1373 def abortJob(self):
1374 pass
1375
1376 def fsFreeze(self):
1377 pass
1378
1379 def fsThaw(self):
1380 pass
1381
1382
1383 class DomainSnapshot(object):
1384 def __init__(self, name, domain):
1385 self._name = name
1386 self._domain = domain
1387
1388 def delete(self, flags):
1389 del self._domain._snapshots[self._name]
1390
1391
1392 class Secret(object):
1393 def __init__(self, connection, xml):
1394 self._connection = connection
1395 self._xml = xml
1396 self._parse_xml(xml)
1397 self._value = None
1398
1399 def _parse_xml(self, xml):
1400 tree = etree.fromstring(xml)
1401 self._uuid = tree.find('./uuid').text
1402 self._private = tree.get('private') == 'yes'
1403
1404 def setValue(self, value, flags=0):
1405 self._value = value
1406 return 0
1407
1408 def value(self, flags=0):
1409 if self._value is None:
1410 raise make_libvirtError(
1411 libvirtError,
1412 "secret '%s' does not have a value" % self._uuid,
1413 error_code=VIR_ERR_NO_SECRET,
1414 error_domain=VIR_FROM_SECRET)
1415 pass
1416
1417 if self._private:
1418 raise make_libvirtError(
1419 libvirtError,
1420 'secret is private',
1421 error_code=VIR_ERR_INVALID_SECRET,
1422 error_domain=VIR_FROM_SECRET)
1423
1424 return self._value
1425
1426 def undefine(self):
1427 self._connection._remove_secret(self)
1428
1429
1430 class Connection(object):
1431 def __init__(self, uri=None, readonly=False, version=FAKE_LIBVIRT_VERSION,
1432 hv_version=FAKE_QEMU_VERSION, host_info=None, pci_info=None,
1433 mdev_info=None, hostname=None):
1434 if not uri or uri == '':
1435 if allow_default_uri_connection:
1436 uri = 'qemu:///session'
1437 else:
1438 raise ValueError("URI was None, but fake libvirt is "
1439 "configured to not accept this.")
1440
1441 uri_whitelist = ['qemu:///system',
1442 'qemu:///session',
1443 'lxc:///', # from LibvirtDriver._uri()
1444 'xen:///', # from LibvirtDriver._uri()
1445 'uml:///system',
1446 'test:///default',
1447 'parallels:///system']
1448
1449 if uri not in uri_whitelist:
1450 raise make_libvirtError(
1451 libvirtError,
1452 "libvirt error: no connection driver "
1453 "available for No connection for URI %s" % uri,
1454 error_code=5, error_domain=0)
1455
1456 self.readonly = readonly
1457 self._uri = uri
1458 self._vms = {}
1459 self._running_vms = {}
1460 self._id_counter = 1 # libvirt reserves 0 for the hypervisor.
1461 self._nodedevs = {}
1462 self._secrets = {}
1463 self._event_callbacks = {}
1464 self.fakeLibVersion = version
1465 self.fakeVersion = hv_version
1466 self.host_info = host_info or HostInfo()
1467 self.pci_info = pci_info or HostPCIDevicesInfo(num_pci=0,
1468 num_pfs=0,
1469 num_vfs=0)
1470 self.mdev_info = mdev_info or HostMdevDevicesInfo(devices={})
1471 self.hostname = hostname or 'compute1'
1472
1473 def _add_nodedev(self, nodedev):
1474 self._nodedevs[nodedev._name] = nodedev
1475
1476 def _remove_nodedev(self, nodedev):
1477 del self._nodedevs[nodedev._name]
1478
1479 def _add_secret(self, secret):
1480 self._secrets[secret._uuid] = secret
1481
1482 def _remove_secret(self, secret):
1483 del self._secrets[secret._uuid]
1484
1485 def _mark_running(self, dom):
1486 self._running_vms[self._id_counter] = dom
1487 self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STARTED, 0)
1488 self._id_counter += 1
1489
1490 def _mark_not_running(self, dom):
1491 if dom._transient:
1492 self._undefine(dom)
1493
1494 dom._id = -1
1495
1496 for (k, v) in self._running_vms.items():
1497 if v == dom:
1498 del self._running_vms[k]
1499 self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STOPPED, 0)
1500 return
1501
1502 def _undefine(self, dom):
1503 del self._vms[dom.name()]
1504 if not dom._transient:
1505 self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_UNDEFINED, 0)
1506
1507 def getInfo(self):
1508 return [self.host_info.arch,
1509 self.host_info.kB_mem,
1510 self.host_info.cpus,
1511 self.host_info.cpu_mhz,
1512 self.host_info.cpu_nodes,
1513 self.host_info.cpu_sockets,
1514 self.host_info.cpu_cores,
1515 self.host_info.cpu_threads]
1516
1517 def lookupByUUIDString(self, uuid):
1518 for vm in self._vms.values():
1519 if vm.UUIDString() == uuid:
1520 return vm
1521 raise make_libvirtError(
1522 libvirtError,
1523 'Domain not found: no domain with matching uuid "%s"' % uuid,
1524 error_code=VIR_ERR_NO_DOMAIN,
1525 error_domain=VIR_FROM_QEMU)
1526
1527 def listAllDomains(self, flags=None):
1528 vms = []
1529 for vm in self._vms.values():
1530 if flags & VIR_CONNECT_LIST_DOMAINS_ACTIVE:
1531 if vm._state != VIR_DOMAIN_SHUTOFF:
1532 vms.append(vm)
1533 if flags & VIR_CONNECT_LIST_DOMAINS_INACTIVE:
1534 if vm._state == VIR_DOMAIN_SHUTOFF:
1535 vms.append(vm)
1536 return vms
1537
1538 def _emit_lifecycle(self, dom, event, detail):
1539 if VIR_DOMAIN_EVENT_ID_LIFECYCLE not in self._event_callbacks:
1540 return
1541
1542 cbinfo = self._event_callbacks[VIR_DOMAIN_EVENT_ID_LIFECYCLE]
1543 callback = cbinfo[0]
1544 opaque = cbinfo[1]
1545 callback(self, dom, event, detail, opaque)
1546
1547 def defineXML(self, xml):
1548 dom = Domain(connection=self, running=False, transient=False, xml=xml)
1549 self._vms[dom.name()] = dom
1550 self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_DEFINED, 0)
1551 return dom
1552
1553 def createXML(self, xml, flags):
1554 dom = Domain(connection=self, running=True, transient=True, xml=xml)
1555 self._vms[dom.name()] = dom
1556 self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STARTED, 0)
1557 return dom
1558
1559 def getType(self):
1560 if self._uri == 'qemu:///system':
1561 return 'QEMU'
1562
1563 def getLibVersion(self):
1564 return self.fakeLibVersion
1565
1566 def getVersion(self):
1567 return self.fakeVersion
1568
1569 def getHostname(self):
1570 return self.hostname
1571
1572 def domainEventRegisterAny(self, dom, eventid, callback, opaque):
1573 self._event_callbacks[eventid] = [callback, opaque]
1574
1575 def registerCloseCallback(self, cb, opaque):
1576 pass
1577
1578 def getCPUMap(self):
1579 """Return calculated CPU map from HostInfo, by default showing 2
1580 online CPUs.
1581 """
1582 total_cpus = self.host_info.cpus
1583 cpu_map = [True for cpu_num in range(total_cpus)]
1584 return (total_cpus, cpu_map, total_cpus)
1585
1586 def getDomainCapabilities(self, emulatorbin, arch, machine_type,
1587 virt_type, flags):
1588 """Return spoofed domain capabilities."""
1589 if arch in fake_libvirt_data.STATIC_DOMCAPABILITIES:
1590 return fake_libvirt_data.STATIC_DOMCAPABILITIES[arch]
1591
1592 if arch == 'x86_64':
1593 aliases = {'pc': 'pc-i440fx-2.11', 'q35': 'pc-q35-2.11'}
1594 return fake_libvirt_data.DOMCAPABILITIES_X86_64_TEMPLATE % \
1595 {'features': self._domain_capability_features,
1596 'mtype': aliases.get(machine_type, machine_type)}
1597
1598 raise Exception("fakelibvirt doesn't support getDomainCapabilities "
1599 "for %s architecture" % arch)
1600
1601 def getCPUModelNames(self, arch):
1602 mapping = {
1603 'x86_64': [
1604 '486',
1605 'pentium',
1606 'pentium2',
1607 'pentium3',
1608 'pentiumpro',
1609 'coreduo',
1610 'n270',
1611 'core2duo',
1612 'qemu32',
1613 'kvm32',
1614 'cpu64-rhel5',
1615 'cpu64-rhel6',
1616 'qemu64',
1617 'kvm64',
1618 'Conroe',
1619 'Penryn',
1620 'Nehalem',
1621 'Nehalem-IBRS',
1622 'Westmere',
1623 'Westmere-IBRS',
1624 'SandyBridge',
1625 'SandyBridge-IBRS',
1626 'IvyBridge',
1627 'IvyBridge-IBRS',
1628 'Haswell-noTSX',
1629 'Haswell-noTSX-IBRS',
1630 'Haswell',
1631 'Haswell-IBRS',
1632 'Broadwell-noTSX',
1633 'Broadwell-noTSX-IBRS',
1634 'Broadwell',
1635 'Broadwell-IBRS',
1636 'Skylake-Client',
1637 'Skylake-Client-IBRS',
1638 'Skylake-Server',
1639 'Skylake-Server-IBRS',
1640 'Cascadelake-Server',
1641 'Icelake-Client',
1642 'Icelake-Server',
1643 'athlon',
1644 'phenom',
1645 'Opteron_G1',
1646 'Opteron_G2',
1647 'Opteron_G3',
1648 'Opteron_G4',
1649 'Opteron_G5',
1650 'EPYC',
1651 'EPYC-IBPB'],
1652 'ppc64': [
1653 'POWER6',
1654 'POWER7',
1655 'POWER8',
1656 'POWER9',
1657 'POWERPC_e5500',
1658 'POWERPC_e6500']
1659 }
1660 return mapping.get(arch, [])
1661
1662 # Features are kept separately so that the tests can patch this
1663 # class variable with alternate values.
1664 _domain_capability_features = ''' <features>
1665 <gic supported='no'/>
1666 </features>'''
1667
1668 _domain_capability_features_with_SEV = ''' <features>
1669 <gic supported='no'/>
1670 <sev supported='yes'>
1671 <cbitpos>47</cbitpos>
1672 <reducedPhysBits>1</reducedPhysBits>
1673 </sev>
1674 </features>'''
1675
1676 _domain_capability_features_with_SEV_unsupported = \
1677 _domain_capability_features_with_SEV.replace('yes', 'no')
1678
1679 def getCapabilities(self):
1680 """Return spoofed capabilities."""
1681 numa_topology = self.host_info.numa_topology
1682 if isinstance(numa_topology, vconfig.LibvirtConfigCapsNUMATopology):
1683 numa_topology = numa_topology.to_xml()
1684
1685 return (fake_libvirt_data.CAPABILITIES_TEMPLATE
1686 % {'sockets': self.host_info.cpu_sockets,
1687 'cores': self.host_info.cpu_cores,
1688 'threads': self.host_info.cpu_threads,
1689 'topology': numa_topology})
1690
1691 def compareCPU(self, xml, flags):
1692 tree = etree.fromstring(xml)
1693
1694 arch_node = tree.find('./arch')
1695 if arch_node is not None:
1696 if arch_node.text not in [obj_fields.Architecture.X86_64,
1697 obj_fields.Architecture.I686]:
1698 return VIR_CPU_COMPARE_INCOMPATIBLE
1699
1700 model_node = tree.find('./model')
1701 if model_node is not None:
1702 # arch_node may not present, therefore query all cpu models.
1703 if model_node.text not in self.getCPUModelNames('x86_64') and \
1704 model_node.text not in self.getCPUModelNames('ppc64'):
1705 raise make_libvirtError(
1706 libvirtError,
1707 "internal error: Unknown CPU model %s" % model_node.text,
1708 error_code = VIR_ERR_INTERNAL_ERROR,
1709 error_domain=VIR_FROM_QEMU)
1710 if model_node.text != self.host_info.cpu_model:
1711 return VIR_CPU_COMPARE_INCOMPATIBLE
1712
1713 vendor_node = tree.find('./vendor')
1714 if vendor_node is not None:
1715 if vendor_node.text != self.host_info.cpu_vendor:
1716 return VIR_CPU_COMPARE_INCOMPATIBLE
1717
1718 # The rest of the stuff libvirt implements is rather complicated
1719 # and I don't think it adds much value to replicate it here.
1720
1721 return VIR_CPU_COMPARE_IDENTICAL
1722
1723 def getCPUStats(self, cpuNum, flag):
1724 if cpuNum < 2:
1725 return {'kernel': 5664160000000,
1726 'idle': 1592705190000000,
1727 'user': 26728850000000,
1728 'iowait': 6121490000000}
1729 else:
1730 raise make_libvirtError(
1731 libvirtError,
1732 "invalid argument: Invalid cpu number",
1733 error_code=VIR_ERR_INTERNAL_ERROR,
1734 error_domain=VIR_FROM_QEMU)
1735
1736 def device_lookup_by_name(self, dev_name):
1737 return self.pci_info.get_device_by_name(dev_name)
1738
1739 def nodeDeviceLookupByName(self, name):
1740 if name.startswith('mdev'):
1741 return self.mdev_info.get_device_by_name(name)
1742
1743 pci_dev = self.pci_info.get_device_by_name(name)
1744 if pci_dev:
1745 return pci_dev
1746 try:
1747 return self._nodedevs[name]
1748 except KeyError:
1749 raise make_libvirtError(
1750 libvirtError,
1751 "no nodedev with matching name %s" % name,
1752 error_code=VIR_ERR_NO_NODE_DEVICE,
1753 error_domain=VIR_FROM_NODEDEV)
1754
1755 def listDevices(self, cap, flags):
1756 if cap == 'pci':
1757 return self.pci_info.get_all_devices()
1758 if cap == 'mdev':
1759 return self.mdev_info.get_all_devices()
1760 if cap == 'mdev_types':
1761 return self.pci_info.get_all_mdev_capable_devices()
1762 else:
1763 raise ValueError('Capability "%s" is not supported' % cap)
1764
1765 def baselineCPU(self, cpu, flag):
1766 """Add new libvirt API."""
1767 return """<cpu mode='custom' match='exact'>
1768 <model>Penryn</model>
1769 <vendor>Intel</vendor>
1770 <feature name='xtpr'/>
1771 <feature name='tm2'/>
1772 <feature name='est'/>
1773 <feature name='vmx'/>
1774 <feature name='ds_cpl'/>
1775 <feature name='monitor'/>
1776 <feature name='pbe'/>
1777 <feature name='tm'/>
1778 <feature name='ht'/>
1779 <feature name='ss'/>
1780 <feature name='acpi'/>
1781 <feature name='ds'/>
1782 <feature name='vme'/>
1783 <feature policy='require' name='aes'/>
1784 </cpu>"""
1785
1786 def secretLookupByUsage(self, usage_type_obj, usage_id):
1787 pass
1788
1789 def secretDefineXML(self, xml):
1790 secret = Secret(self, xml)
1791 self._add_secret(secret)
1792 return secret
1793
1794 def listAllDevices(self, flags):
1795 # Note this is incomplete as we do not filter
1796 # based on the flags however it is enough for our
1797 # current testing.
1798 return [NodeDevice(self, xml=dev.XMLDesc(0))
1799 for dev in self.pci_info.devices.values()]
1800
1801
1802 def openAuth(uri, auth, flags=0):
1803
1804 if type(auth) != list:
1805 raise Exception("Expected a list for 'auth' parameter")
1806
1807 if type(auth[0]) != list:
1808 raise Exception("Expected a function in 'auth[0]' parameter")
1809
1810 if not callable(auth[1]):
1811 raise Exception("Expected a function in 'auth[1]' parameter")
1812
1813 return Connection(uri, (flags == VIR_CONNECT_RO))
1814
1815
1816 def virEventRunDefaultImpl():
1817 time.sleep(1)
1818
1819
1820 def virEventRegisterDefaultImpl():
1821 if connection_used:
1822 raise Exception("virEventRegisterDefaultImpl() must be "
1823 "called before connection is used.")
1824
1825
1826 def registerErrorHandler(handler, ctxt):
1827 pass
1828
1829
1830 def make_libvirtError(error_class, msg, error_code=None,
1831 error_domain=None, error_message=None,
1832 error_level=None, str1=None, str2=None, str3=None,
1833 int1=None, int2=None):
1834 """Convenience function for creating `libvirtError` exceptions which
1835 allow you to specify arguments in constructor without having to manipulate
1836 the `err` tuple directly.
1837
1838 We need to pass in `error_class` to this function because it may be
1839 `libvirt.libvirtError` or `fakelibvirt.libvirtError` depending on whether
1840 `libvirt-python` is installed.
1841 """
1842 exc = error_class(msg)
1843 exc.err = (error_code, error_domain, error_message, error_level,
1844 str1, str2, str3, int1, int2)
1845 return exc
1846
1847
1848 virDomain = Domain
1849 virNodeDevice = NodeDevice
1850
1851 virConnect = Connection
1852 virSecret = Secret
1853
1854
1855 # A private libvirt-python class and global only provided here for testing to
1856 # ensure it's not returned by libvirt.host.Host.get_libvirt_proxy_classes.
1857 class FakeHandler(object):
1858 def __init__(self):
1859 pass
1860
1861
1862 _EventAddHandleFunc = FakeHandler
1863
1864
1865 class FakeLibvirtFixture(fixtures.Fixture):
1866 """Performs global setup/stubbing for all libvirt tests.
1867 """
1868 def __init__(self, stub_os_vif=True):
1869 self.stub_os_vif = stub_os_vif
1870
1871 def setUp(self):
1872 super(FakeLibvirtFixture, self).setUp()
1873
1874 # Some modules load the libvirt library in a strange way
1875 for module in ('driver', 'host', 'guest', 'migration'):
1876 i = 'nova.virt.libvirt.{module}.libvirt'.format(module=module)
1877 # NOTE(mdbooth): The strange incantation below means 'this module'
1878 self.useFixture(fixtures.MonkeyPatch(i, sys.modules[__name__]))
1879
1880 self.useFixture(
1881 fixtures.MockPatch('nova.virt.libvirt.utils.get_fs_info'))
1882 self.useFixture(
1883 fixtures.MockPatch('nova.compute.utils.get_machine_ips'))
1884
1885 # libvirt driver needs to call out to the filesystem to get the
1886 # parent_ifname for the SRIOV VFs.
1887 self.useFixture(fixtures.MockPatch(
1888 'nova.pci.utils.get_ifname_by_pci_address',
1889 return_value='fake_pf_interface_name'))
1890
1891 # libvirt calls out to sysfs to get the vfs ID during macvtap plug
1892 self.useFixture(fixtures.MockPatch(
1893 'nova.pci.utils.get_vf_num_by_pci_address', return_value=1))
1894
1895 # libvirt calls out to privsep to set the mac and vlan of a macvtap
1896 self.useFixture(fixtures.MockPatch(
1897 'nova.privsep.linux_net.set_device_macaddr_and_vlan'))
1898
1899 # libvirt calls out to privsep to set the port state during macvtap
1900 # plug
1901 self.useFixture(fixtures.MockPatch(
1902 'nova.privsep.linux_net.set_device_macaddr'))
1903
1904 # Don't assume that the system running tests has a valid machine-id
1905 self.useFixture(fixtures.MockPatch(
1906 'nova.virt.libvirt.driver.LibvirtDriver'
1907 '._get_host_sysinfo_serial_os', return_value=uuids.machine_id))
1908
1909 # Stub out _log_host_capabilities since it logs a giant string at INFO
1910 # and we don't want that to blow up the subunit parser in test runs.
1911 self.useFixture(fixtures.MockPatch(
1912 'nova.virt.libvirt.host.Host._log_host_capabilities'))
1913
1914 disable_event_thread(self)
1915
1916 if self.stub_os_vif:
1917 # Make sure to never try and actually plug/unplug VIFs in os-vif
1918 # unless we're explicitly testing that code and the test itself
1919 # will handle the appropriate mocking.
1920 self.useFixture(fixtures.MonkeyPatch(
1921 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver._plug_os_vif',
1922 lambda *a, **kw: None))
1923 self.useFixture(fixtures.MonkeyPatch(
1924 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver._unplug_os_vif',
1925 lambda *a, **kw: None))
1926
1927 # os_vif.initialize is typically done in nova-compute startup
1928 # even if we are not planning to plug anything with os_vif in the test
1929 # we still need the object model initialized to be able to generate
1930 # guest config xml properly
1931 import os_vif
1932 os_vif.initialize()