Coverage for nova/virt/libvirt/blockinfo.py: 97%
287 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
1# Copyright (C) 2012-2013 Red Hat, Inc.
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.
15"""
16Handling of block device information and mapping.
18This module contains helper methods for interpreting the block
19device information and determining the suitable mapping to
20guest devices and libvirt XML.
22Throughout these methods there are a number of standard
23variables / types used
25 * 'mapping': a dict contains the storage device mapping.
27 For the default disk types it will contain the following
28 keys & values:
30 'disk' -> disk_info
31 'disk.rescue' -> disk_info
32 'disk.local' -> disk_info
33 'disk.swap' -> disk_info
34 'disk.config' -> disk_info
36 If any of the default disks are overridden by the block
37 device info mappings, the hash value will be None
39 For any ephemeral device there will also be a dict entry
41 'disk.eph$NUM' -> disk_info
43 For any volume device there will also be a dict entry:
45 $path -> disk_info
47 Finally a special key will refer to the root device:
49 'root' -> disk_info
52 * 'disk_info': a dict specifying disk configuration
54 It contains the following 3 required fields
56 bus (disk_bus), dev (disk_dev), type (device_type)
58 and possibly these optional fields: ('format', 'boot_index')
60 * 'disk_bus': the guest bus type ('ide', 'virtio', 'scsi', etc)
62 * 'disk_dev': the device name 'vda', 'hdc', 'sdf', etc
64 * 'device_type': type of device eg 'disk', 'cdrom', 'floppy'
66 * 'format': Which format to apply to the device if applicable
68 * 'boot_index': Number designating the boot order of the device
70"""
72import copy
73import itertools
74import operator
76from oslo_config import cfg
77from oslo_serialization import jsonutils
80from nova import block_device
81from nova import exception
82from nova.i18n import _
83from nova.objects import base as obj_base
84from nova.objects import fields as obj_fields
85from nova.virt import configdrive
86from nova.virt import driver
87from nova.virt.libvirt import utils as libvirt_utils
88from nova.virt import osinfo
90CONF = cfg.CONF
92BOOT_DEV_FOR_TYPE = {'disk': 'hd', 'cdrom': 'cdrom', 'floppy': 'fd',
93 'lun': 'hd'}
94# NOTE(aspiers): If you change this, don't forget to update the docs and
95# metadata for hw_*_bus in glance.
96SUPPORTED_DEVICE_BUSES = {
97 'qemu': ['virtio', 'scsi', 'ide', 'usb', 'fdc', 'sata'],
98 'kvm': ['virtio', 'scsi', 'ide', 'usb', 'fdc', 'sata'],
99 'lxc': ['lxc'],
100 'parallels': ['ide', 'scsi'],
101 # we no longer support UML or Xen, but we keep track of their bus types so
102 # we can reject them for other virt types
103 'xen': ['xen', 'ide'],
104 'uml': ['uml'],
105}
106SUPPORTED_DEVICE_TYPES = ('disk', 'cdrom', 'floppy', 'lun')
109def has_disk_dev(mapping, disk_dev):
110 """Determine if a disk device name has already been used.
112 Looks at all the keys in mapping to see if any
113 corresponding disk_info tuple has a device name
114 matching disk_dev
116 Returns True if the disk_dev is in use.
117 """
119 for disk in mapping:
120 info = mapping[disk]
121 if info['dev'] == disk_dev:
122 return True
123 return False
126def get_dev_prefix_for_disk_bus(disk_bus):
127 """Determine the dev prefix for a disk bus.
129 Determine the dev prefix to be combined
130 with a disk number to fix a disk_dev.
131 eg 'hd' for 'ide' bus can be used to
132 form a disk dev 'hda'
134 Returns the dev prefix or raises an
135 exception if the disk bus is unknown.
136 """
138 if CONF.libvirt.disk_prefix:
139 return CONF.libvirt.disk_prefix
140 if disk_bus == "ide":
141 return "hd"
142 elif disk_bus == "virtio":
143 return "vd"
144 elif disk_bus == "scsi":
145 return "sd"
146 elif disk_bus == "usb":
147 return "sd"
148 elif disk_bus == "fdc":
149 return "fd"
150 elif disk_bus == "lxc":
151 return None
152 elif disk_bus == "sata": 152 ↛ 155line 152 didn't jump to line 155 because the condition on line 152 was always true
153 return "sd"
154 else:
155 raise exception.InternalError(
156 _("Unable to determine disk prefix for %s") %
157 disk_bus)
160def get_dev_count_for_disk_bus(disk_bus):
161 """Determine the number disks supported.
163 Determine how many disks can be supported in
164 a single VM for a particular disk bus.
166 Returns the number of disks supported or None
167 if there is no limit.
168 """
170 if disk_bus == "ide":
171 return 4
172 else:
173 if CONF.compute.max_disk_devices_to_attach < 0: 173 ↛ 175line 173 didn't jump to line 175 because the condition on line 173 was always true
174 return None
175 return CONF.compute.max_disk_devices_to_attach
178def find_disk_dev_for_disk_bus(mapping, bus,
179 assigned_devices=None):
180 """Identify a free disk dev name for a bus.
182 Determines the possible disk dev names for
183 the bus, and then checks them in order until
184 it identifies one that is not yet used in the
185 disk mapping.
187 Returns the chosen disk_dev name, or raises an
188 exception if none is available.
189 """
191 dev_prefix = get_dev_prefix_for_disk_bus(bus)
192 if dev_prefix is None:
193 return None
195 if assigned_devices is None:
196 assigned_devices = []
198 max_dev = get_dev_count_for_disk_bus(bus)
200 idx = 0
201 while True:
202 if max_dev is not None and idx >= max_dev:
203 raise exception.TooManyDiskDevices(maximum=max_dev)
204 disk_dev = block_device.generate_device_name(dev_prefix, idx)
205 if not has_disk_dev(mapping, disk_dev):
206 if disk_dev not in assigned_devices:
207 return disk_dev
208 idx += 1
210 raise exception.TooManyDiskDevices(maximum=max_dev)
213def is_disk_bus_valid_for_virt(virt_type, disk_bus):
214 if virt_type not in SUPPORTED_DEVICE_BUSES: 214 ↛ 215line 214 didn't jump to line 215 because the condition on line 214 was never true
215 raise exception.UnsupportedVirtType(virt=virt_type)
216 return disk_bus in SUPPORTED_DEVICE_BUSES[virt_type]
219def get_disk_bus_for_device_type(instance,
220 virt_type,
221 image_meta,
222 device_type="disk"):
223 """Determine the best disk bus to use for a device type.
225 Considering the currently configured virtualization type, return the
226 optimal disk_bus to use for a given device type. For example, for a disk
227 on KVM it will return 'virtio', while for a CDROM, it will return 'ide'
228 for the 'pc' machine type on x86_64, 'sata' for the 'q35' machine type on
229 x86_64 and 'scsi' on ppc64.
231 Returns the disk_bus, or returns None if the device type is not supported
232 for this virtualization
233 """
235 # Prefer a disk bus set against the image first of all
236 if device_type == "disk":
237 disk_bus = osinfo.HardwareProperties(image_meta).disk_model
238 else:
239 key = "hw_" + device_type + "_bus"
240 disk_bus = image_meta.properties.get(key)
241 if disk_bus is not None:
242 if not is_disk_bus_valid_for_virt(virt_type, disk_bus):
243 raise exception.UnsupportedHardware(model=disk_bus,
244 virt=virt_type)
245 return disk_bus
247 # Otherwise pick a hypervisor default disk bus
248 if virt_type in ("qemu", "kvm"):
249 if device_type == "cdrom":
250 guestarch = libvirt_utils.get_arch(image_meta)
251 if guestarch in (
252 obj_fields.Architecture.PPC,
253 obj_fields.Architecture.PPC64,
254 obj_fields.Architecture.PPCLE,
255 obj_fields.Architecture.PPC64LE,
256 obj_fields.Architecture.S390,
257 obj_fields.Architecture.S390X,
258 obj_fields.Architecture.AARCH64):
259 return "scsi"
260 machine_type = libvirt_utils.get_machine_type(image_meta)
261 # NOTE(lyarwood): We can't be any more explicit here as QEMU
262 # provides a version of the Q35 machine type per release.
263 # Additionally downstream distributions can also provide their own.
264 if machine_type and 'q35' in machine_type:
265 # NOTE(lyarwood): The Q35 machine type does not provide an IDE
266 # bus and as such we must use a SATA bus for cdroms.
267 return "sata"
268 else:
269 return "ide"
270 elif device_type == "disk":
271 return "virtio"
272 elif device_type == "floppy": 272 ↛ 285line 272 didn't jump to line 285 because the condition on line 272 was always true
273 return "fdc"
274 elif virt_type == "lxc":
275 return "lxc"
276 elif virt_type == "parallels":
277 if device_type == "cdrom":
278 return "ide"
279 elif device_type == "disk": 279 ↛ 285line 279 didn't jump to line 285 because the condition on line 279 was always true
280 return "scsi"
281 else:
282 # If virt-type not in list then it is unsupported
283 raise exception.UnsupportedVirtType(virt=virt_type)
285 return None
288def get_disk_bus_for_disk_dev(virt_type, disk_dev):
289 """Determine the disk bus for a disk device.
291 Given a disk device like 'hda' or 'sdf', guess what the most appropriate
292 disk bus is for the currently configured virtualization technology
294 :return: The preferred disk bus for the given disk prefix.
295 :raises: InternalError if the disk device prefix is unknown.
296 """
298 if disk_dev.startswith('hd'):
299 return "ide"
300 elif disk_dev.startswith('sd'):
301 # Reverse mapping 'sd' is not reliable
302 # there are many possible mappings. So
303 # this picks the most likely mappings
304 return "scsi"
305 elif disk_dev.startswith('vd'):
306 return "virtio"
307 elif disk_dev.startswith('fd'):
308 return "fdc"
309 else:
310 msg = _("Unable to determine disk bus for '%s'") % disk_dev[:1]
311 raise exception.InternalError(msg)
314def get_next_disk_info(mapping, disk_bus,
315 device_type='disk',
316 boot_index=None,
317 assigned_devices=None):
318 """Determine the disk info for the next device on disk_bus.
320 Considering the disks already listed in the disk mapping,
321 determine the next available disk dev that can be assigned
322 for the disk bus.
324 Returns the disk_info for the next available disk.
325 """
327 disk_dev = find_disk_dev_for_disk_bus(mapping,
328 disk_bus,
329 assigned_devices)
330 info = {'bus': disk_bus,
331 'dev': disk_dev,
332 'type': device_type}
334 if boot_index is not None and boot_index >= 0:
335 info['boot_index'] = str(boot_index)
337 return info
340def get_eph_disk(index):
341 return 'disk.eph' + str(index)
344def get_config_drive_type():
345 """Determine the type of config drive.
347 If config_drive_format is set to iso9660 then the config drive will
348 be 'cdrom', otherwise 'disk'.
350 Returns a string indicating the config drive type.
351 """
353 if CONF.config_drive_format == 'iso9660':
354 config_drive_type = 'cdrom'
355 elif CONF.config_drive_format == 'vfat': 355 ↛ 358line 355 didn't jump to line 358 because the condition on line 355 was always true
356 config_drive_type = 'disk'
357 else:
358 raise exception.ConfigDriveUnknownFormat(
359 format=CONF.config_drive_format)
361 return config_drive_type
364def get_info_from_bdm(instance, virt_type, image_meta, bdm,
365 mapping=None, disk_bus=None,
366 dev_type=None, allowed_types=None,
367 assigned_devices=None):
368 mapping = mapping or {}
369 allowed_types = allowed_types or SUPPORTED_DEVICE_TYPES
370 device_name = block_device.strip_dev(get_device_name(bdm))
372 bdm_type = bdm.get('device_type') or dev_type
373 if bdm_type not in allowed_types:
374 bdm_type = 'disk'
376 bdm_bus = bdm.get('disk_bus') or disk_bus
377 if not is_disk_bus_valid_for_virt(virt_type, bdm_bus):
378 if device_name:
379 bdm_bus = get_disk_bus_for_disk_dev(virt_type, device_name)
380 else:
381 bdm_bus = get_disk_bus_for_device_type(instance, virt_type,
382 image_meta, bdm_type)
384 if not device_name:
385 if assigned_devices:
386 padded_mapping = {dev: {'dev': dev} for dev in assigned_devices}
387 padded_mapping.update(mapping)
388 else:
389 padded_mapping = mapping
391 device_name = find_disk_dev_for_disk_bus(padded_mapping, bdm_bus)
393 bdm_info = {'bus': bdm_bus,
394 'dev': device_name,
395 'type': bdm_type}
397 bdm_format = bdm.get('guest_format')
398 if bdm_format:
399 bdm_info.update({'format': bdm_format})
401 boot_index = bdm.get('boot_index')
402 if boot_index is not None and boot_index >= 0:
403 # NOTE(ndipanov): libvirt starts ordering from 1, not 0
404 bdm_info['boot_index'] = str(boot_index + 1)
406 # If the device is encrypted pass through the secret, format and options
407 if bdm.get('encrypted'):
408 bdm_info['encrypted'] = bdm.get('encrypted')
409 bdm_info['encryption_secret_uuid'] = bdm.get('encryption_secret_uuid')
410 bdm_info['encryption_format'] = bdm.get('encryption_format')
411 encryption_options = bdm.get('encryption_options')
412 if encryption_options:
413 bdm_info['encryption_options'] = jsonutils.loads(
414 encryption_options)
416 return bdm_info
419def get_device_name(bdm):
420 """Get the device name if present regardless of the bdm format."""
421 if isinstance(bdm, obj_base.NovaObject):
422 return bdm.device_name
423 else:
424 return bdm.get('device_name') or bdm.get('mount_device')
427def get_root_info(instance, virt_type, image_meta, root_bdm,
428 disk_bus, cdrom_bus, root_device_name=None):
430 # NOTE(mriedem): In case the image_meta object was constructed from
431 # an empty dict, like in the case of evacuate, we have to first check
432 # if disk_format is set on the ImageMeta object.
433 if is_iso := (image_meta.obj_attr_is_set('disk_format') and
434 image_meta.disk_format == 'iso'):
435 root_device_bus = cdrom_bus
436 root_device_type = 'cdrom'
437 else:
438 root_device_bus = disk_bus
439 root_device_type = 'disk'
441 if root_bdm is None:
442 if not root_device_name:
443 root_device_name = find_disk_dev_for_disk_bus({}, root_device_bus)
444 return {'bus': root_device_bus,
445 'type': root_device_type,
446 'dev': block_device.strip_dev(root_device_name),
447 'boot_index': '1'}
449 root_bdm_copy = copy.deepcopy(root_bdm)
451 if is_iso:
452 root_bdm_copy['disk_bus'] = root_device_bus
453 root_bdm_copy['device_type'] = root_device_type
455 if not get_device_name(root_bdm_copy) and root_device_name:
456 root_bdm_copy['device_name'] = root_device_name
458 return get_info_from_bdm(
459 instance, virt_type, image_meta, root_bdm_copy, {}, disk_bus,
460 )
463def default_device_names(virt_type, context, instance, block_device_info,
464 image_meta):
465 get_disk_info(virt_type, instance, image_meta, block_device_info)
467 for driver_bdm in itertools.chain(
468 block_device_info['image'],
469 block_device_info['ephemerals'],
470 [block_device_info['swap']] if
471 block_device_info['swap'] else [],
472 block_device_info['block_device_mapping']
473 ):
474 driver_bdm.save()
477def get_default_ephemeral_info(instance, disk_bus, block_device_info, mapping):
478 ephemerals = driver.block_device_info_get_ephemerals(block_device_info)
479 if not instance.ephemeral_gb or instance.ephemeral_gb <= 0 or ephemerals:
480 return None
481 else:
482 info = get_next_disk_info(mapping, disk_bus)
483 if block_device.volume_in_mapping(info['dev'], block_device_info):
484 return None
485 return info
488def update_bdm(bdm, info):
489 device_name_field = ('device_name'
490 if 'device_name' in bdm
491 else 'mount_device')
492 # Do not update the device name if it was already present
493 bdm.update(dict(zip((device_name_field,
494 'disk_bus', 'device_type'),
495 ((bdm.get(device_name_field) or
496 block_device.prepend_dev(info['dev'])),
497 info['bus'], info['type']))))
500def get_disk_mapping(virt_type, instance, disk_bus, cdrom_bus, image_meta,
501 block_device_info=None, rescue=False,
502 rescue_image_meta=None):
503 """Determine how to map default disks to the virtual machine.
505 This is about figuring out whether the default 'disk',
506 'disk.local', 'disk.swap' and 'disk.config' images have
507 been overridden by the block device mapping.
509 Returns the guest disk mapping for the devices.
510 """
511 # NOTE(lyarwood): This is a legacy rescue attempt so provide a mapping with
512 # the rescue disk, original root disk and optional config drive.
513 if rescue and rescue_image_meta is None:
514 return _get_rescue_disk_mapping(
515 virt_type, instance, disk_bus, image_meta)
517 # NOTE(lyarwood): This is a new stable rescue attempt so provide a mapping
518 # with the original mapping *and* rescue disk appended to the end.
519 if rescue and rescue_image_meta:
520 return _get_stable_device_rescue_mapping(
521 virt_type, instance, disk_bus, cdrom_bus, image_meta,
522 block_device_info, rescue_image_meta)
524 # NOTE(lyarwood): This is a normal spawn so fetch the full disk mapping.
525 return _get_disk_mapping(
526 virt_type, instance, disk_bus, cdrom_bus, image_meta,
527 block_device_info)
530def _get_rescue_disk_mapping(virt_type, instance, disk_bus, image_meta):
531 """Build disk mapping for a legacy instance rescue
533 This legacy method of rescue requires that the rescue device is attached
534 first, ahead of the original root disk and optional config drive.
536 :param virt_type: Virt type used by libvirt.
537 :param instance: nova.objects.instance.Instance object
538 :param disk_bus: Disk bus to use within the mapping
539 :param image_meta: objects.image_meta.ImageMeta for the instance
541 :returns: Disk mapping for the given instance
542 """
543 mapping = {}
544 rescue_info = get_next_disk_info(mapping,
545 disk_bus, boot_index=1)
546 mapping['disk.rescue'] = rescue_info
547 mapping['root'] = rescue_info
549 os_info = get_next_disk_info(mapping,
550 disk_bus)
551 mapping['disk'] = os_info
553 if configdrive.required_by(instance):
554 device_type = get_config_drive_type()
555 disk_bus = get_disk_bus_for_device_type(instance,
556 virt_type,
557 image_meta,
558 device_type)
559 config_info = get_next_disk_info(mapping,
560 disk_bus,
561 device_type)
562 mapping['disk.config.rescue'] = config_info
564 return mapping
567def _get_disk_mapping(virt_type, instance, disk_bus, cdrom_bus, image_meta,
568 block_device_info):
569 """Build disk mapping for a given instance
571 :param virt_type: Virt type used by libvirt.
572 :param instance: nova.objects.instance.Instance object
573 :param disk_bus: Disk bus to use within the mapping
574 :param cdrom_bus: CD-ROM bus to use within the mapping
575 :param image_meta: objects.image_meta.ImageMeta for the instance
576 :param block_device_info: dict detailing disks and volumes attached
578 :returns: Disk mapping for the given instance.
579 """
580 mapping = {}
582 driver_bdms = itertools.chain(
583 driver.block_device_info_get_image(block_device_info),
584 driver.block_device_info_get_ephemerals(block_device_info),
585 [driver.block_device_info_get_swap(block_device_info)],
586 driver.block_device_info_get_mapping(block_device_info)
587 )
589 pre_assigned_device_names = [
590 block_device.strip_dev(get_device_name(bdm))
591 for bdm in driver_bdms if get_device_name(bdm)
592 ]
594 # Try to find the root driver bdm, either an image based disk or volume
595 root_bdm = None
596 if any(driver.block_device_info_get_image(block_device_info)):
597 root_bdm = driver.block_device_info_get_image(block_device_info)[0]
598 elif driver.block_device_info_get_mapping(block_device_info):
599 root_bdm = block_device.get_root_bdm(
600 driver.block_device_info_get_mapping(block_device_info))
601 root_device_name = block_device.strip_dev(
602 driver.block_device_info_get_root_device(block_device_info))
603 root_info = get_root_info(
604 instance, virt_type, image_meta, root_bdm,
605 disk_bus, cdrom_bus, root_device_name)
606 mapping['root'] = root_info
608 # NOTE (ft): If device name is not set in root bdm, root_info has a
609 # generated one. We have to copy device name to root bdm to prevent its
610 # second generation in loop through bdms. If device name is already
611 # set, nothing is changed.
612 # NOTE(melwitt): root_bdm can be None in the case of a ISO root device, for
613 # example.
614 if root_bdm:
615 update_bdm(root_bdm, root_info)
617 if (
618 driver.block_device_info_get_image(block_device_info) or
619 root_bdm is None
620 ):
621 mapping['disk'] = root_info
623 default_eph = get_default_ephemeral_info(instance, disk_bus,
624 block_device_info, mapping)
625 if default_eph:
626 mapping['disk.local'] = default_eph
628 for idx, eph in enumerate(driver.block_device_info_get_ephemerals(
629 block_device_info)):
630 eph_info = get_info_from_bdm(
631 instance, virt_type, image_meta, eph, mapping, disk_bus,
632 assigned_devices=pre_assigned_device_names)
633 mapping[get_eph_disk(idx)] = eph_info
634 update_bdm(eph, eph_info)
636 swap = driver.block_device_info_get_swap(block_device_info)
637 if swap and swap.get('swap_size', 0) > 0:
638 swap_info = get_info_from_bdm(
639 instance, virt_type, image_meta,
640 swap, mapping, disk_bus)
641 mapping['disk.swap'] = swap_info
642 update_bdm(swap, swap_info)
643 elif instance.get_flavor()['swap'] > 0:
644 swap_info = get_next_disk_info(mapping, disk_bus,
645 assigned_devices=pre_assigned_device_names)
646 if not block_device.volume_in_mapping(swap_info['dev'], 646 ↛ 650line 646 didn't jump to line 650 because the condition on line 646 was always true
647 block_device_info):
648 mapping['disk.swap'] = swap_info
650 block_device_mapping = driver.block_device_info_get_mapping(
651 block_device_info)
653 for bdm in block_device_mapping:
654 vol_info = get_info_from_bdm(
655 instance, virt_type, image_meta, bdm, mapping,
656 assigned_devices=pre_assigned_device_names)
657 mapping[block_device.prepend_dev(vol_info['dev'])] = vol_info
658 update_bdm(bdm, vol_info)
660 if configdrive.required_by(instance):
661 device_type = get_config_drive_type()
662 disk_bus = get_disk_bus_for_device_type(instance,
663 virt_type,
664 image_meta,
665 device_type)
666 config_info = get_next_disk_info(mapping,
667 disk_bus,
668 device_type)
669 mapping['disk.config'] = config_info
670 return mapping
673def _get_stable_device_rescue_mapping(virt_type, instance, disk_bus, cdrom_bus,
674 image_meta, block_device_info, rescue_image_meta):
675 """Build a disk mapping for a given instance and add a rescue device
677 This method builds the original disk mapping of the instance before
678 attaching the rescue device last.
680 :param virt_type: Virt type used by libvirt.
681 :param instance: nova.objects.instance.Instance object
682 :param disk_bus: Disk bus to use within the mapping
683 :param cdrom_bus: CD-ROM bus to use within the mapping
684 :param image_meta: objects.image_meta.ImageMeta for the instance
685 :param block_device_info: dict detailing disks and volumes attached
686 :param rescue_image_meta: objects.image_meta.ImageMeta of the rescue image
688 :returns: Disk mapping dict with rescue device added.
689 """
690 mapping = _get_disk_mapping(
691 virt_type, instance, disk_bus, cdrom_bus, image_meta,
692 block_device_info)
693 rescue_device = get_rescue_device(rescue_image_meta)
694 rescue_bus = get_rescue_bus(instance, virt_type, rescue_image_meta,
695 rescue_device)
696 rescue_info = get_next_disk_info(mapping, rescue_bus,
697 device_type=rescue_device)
698 mapping['disk.rescue'] = rescue_info
699 return mapping
702def get_disk_info(virt_type, instance, image_meta, block_device_info=None,
703 rescue=False, rescue_image_meta=None):
704 """Determine guest disk mapping info.
706 This is a wrapper around get_disk_mapping, which
707 also returns the chosen disk_bus and cdrom_bus.
708 The returned data is in a dict
710 - disk_bus: the bus for harddisks
711 - cdrom_bus: the bus for CDROMs
712 - mapping: the disk mapping
714 Returns the disk mapping disk.
715 """
717 disk_bus = get_disk_bus_for_device_type(instance, virt_type,
718 image_meta, "disk")
719 cdrom_bus = get_disk_bus_for_device_type(instance, virt_type,
720 image_meta, "cdrom")
721 mapping = get_disk_mapping(virt_type, instance,
722 disk_bus, cdrom_bus,
723 image_meta,
724 block_device_info=block_device_info,
725 rescue=rescue,
726 rescue_image_meta=rescue_image_meta)
728 return {'disk_bus': disk_bus,
729 'cdrom_bus': cdrom_bus,
730 'mapping': mapping}
733def get_boot_order(disk_info):
734 boot_mapping = (info for name, info in disk_info['mapping'].items()
735 if name != 'root' and info.get('boot_index') is not None)
736 boot_devs_dup = (BOOT_DEV_FOR_TYPE[dev['type']] for dev in
737 sorted(boot_mapping,
738 key=operator.itemgetter('boot_index')))
740 def uniq(lst):
741 s = set()
742 return [el for el in lst if el not in s and not s.add(el)]
744 return uniq(boot_devs_dup)
747def get_rescue_device(rescue_image_meta):
748 """Find and validate the rescue device
750 :param rescue_image_meta: ImageMeta object provided when rescuing
752 :raises: UnsupportedRescueDevice if the requested device type is not
753 supported
754 :returns: A valid device type to be used during the rescue
755 """
756 rescue_device = rescue_image_meta.properties.get("hw_rescue_device",
757 "disk")
758 if rescue_device not in SUPPORTED_DEVICE_TYPES:
759 raise exception.UnsupportedRescueDevice(device=rescue_device)
760 return rescue_device
763def get_rescue_bus(instance, virt_type, rescue_image_meta, rescue_device):
764 """Find and validate the rescue bus
766 :param instance: The instance to be rescued
767 :param virt_type: The hypervisor the instance will run on
768 :param rescue_image_meta: ImageMeta object provided when rescuing
769 :param rescue_device: The rescue device being used
771 :raises: UnsupportedRescueBus if the requested bus is not
772 supported by the hypervisor
773 :returns: A valid device bus given virt_type and rescue device
774 """
775 rescue_bus = rescue_image_meta.properties.get("hw_rescue_bus")
776 if rescue_bus is not None:
777 if is_disk_bus_valid_for_virt(virt_type, rescue_bus):
778 return rescue_bus
779 else:
780 raise exception.UnsupportedRescueBus(bus=rescue_bus,
781 virt=virt_type)
782 return get_disk_bus_for_device_type(instance, virt_type, rescue_image_meta,
783 device_type=rescue_device)