]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/disk.py
6 from ceph_volume
import process
7 from ceph_volume
.api
import lvm
8 from ceph_volume
.util
.system
import get_file_contents
11 logger
= logging
.getLogger(__name__
)
14 # The blkid CLI tool has some oddities which prevents having one common call
15 # to extract the information instead of having separate utilities. The `udev`
16 # type of output is needed in older versions of blkid (v 2.23) that will not
17 # work correctly with just the ``-p`` flag to bypass the cache for example.
18 # Xenial doesn't have this problem as it uses a newer blkid version.
21 def get_partuuid(device
):
23 If a device is a partition, it will probably have a PARTUUID on it that
24 will persist and can be queried against `blkid` later to detect the actual
27 out
, err
, rc
= process
.call(
28 ['blkid', '-c', '/dev/null', '-s', 'PARTUUID', '-o', 'value', device
]
30 return ' '.join(out
).strip()
33 def _blkid_parser(output
):
35 Parses the output from a system ``blkid`` call, requires output to be
36 produced using the ``-p`` flag which bypasses the cache, mangling the
37 names. These names are corrected to what it would look like without the
42 /dev/sdb1: UUID="62416664-cbaf-40bd-9689-10bd337379c3" TYPE="xfs" [...]
44 # first spaced separated item is garbage, gets tossed:
45 output
= ' '.join(output
.split()[1:])
46 # split again, respecting possible whitespace in quoted values
47 pairs
= output
.split('" ')
53 'PART_ENTRY_NAME': 'PARTLABEL',
54 'PART_ENTRY_UUID': 'PARTUUID',
55 'PART_ENTRY_TYPE': 'PARTTYPE',
61 column
, value
= pair
.split('=')
64 raw
[column
] = value
.strip().strip().strip('"')
66 for key
, value
in raw
.items():
67 new_key
= mapping
.get(key
)
70 processed
[new_key
] = value
77 The blkid interface to its CLI, creating an output similar to what is
78 expected from ``lsblk``. In most cases, ``lsblk()`` should be the preferred
79 method for extracting information about a device. There are some corner
80 cases where it might provide information that is otherwise unavailable.
82 The system call uses the ``-p`` flag which bypasses the cache, the caveat
83 being that the keys produced are named completely different to expected
86 For example, instead of ``PARTLABEL`` it provides a ``PART_ENTRY_NAME``.
87 A bit of translation between these known keys is done, which is why
88 ``lsblk`` should always be preferred: the output provided here is not as
89 rich, given that a translation of keys is required for a uniform interface
92 Label name to expected output chart:
94 cache bypass name expected name
98 PART_ENTRY_NAME PARTLABEL
99 PART_ENTRY_UUID PARTUUID
101 out
, err
, rc
= process
.call(
102 ['blkid', '-c', '/dev/null', '-p', device
]
104 return _blkid_parser(' '.join(out
))
107 def get_part_entry_type(device
):
109 Parses the ``ID_PART_ENTRY_TYPE`` from the "low level" (bypasses the cache)
110 output that uses the ``udev`` type of output. This output is intended to be
111 used for udev rules, but it is useful in this case as it is the only
112 consistent way to retrieve the GUID used by ceph-disk to identify devices.
114 out
, err
, rc
= process
.call(['blkid', '-c', '/dev/null', '-p', '-o', 'udev', device
])
116 if 'ID_PART_ENTRY_TYPE=' in line
:
117 return line
.split('=')[-1].strip()
121 def get_device_from_partuuid(partuuid
):
123 If a device has a partuuid, query blkid so that it can tell us what that
126 out
, err
, rc
= process
.call(
127 ['blkid', '-c', '/dev/null', '-t', 'PARTUUID="%s"' % partuuid
, '-o', 'device']
129 return ' '.join(out
).strip()
132 def remove_partition(device
):
134 Removes a partition using parted
136 :param device: A ``Device()`` object
138 # Sometimes there's a race condition that makes 'ID_PART_ENTRY_NUMBER' be not present
139 # in the output of `udevadm info --query=property`.
140 # Probably not ideal and not the best fix but this allows to get around that issue.
141 # The idea is to make it retry multiple times before actually failing.
143 udev_info
= udevadm_property(device
.path
)
144 partition_number
= udev_info
.get('ID_PART_ENTRY_NUMBER')
148 if not partition_number
:
149 raise RuntimeError('Unable to detect the partition number for device: %s' % device
.path
)
152 ['parted', device
.parent_device
, '--script', '--', 'rm', partition_number
]
156 def _stat_is_device(stat_obj
):
158 Helper function that will interpret ``os.stat`` output directly, so that other
159 functions can call ``os.stat`` once and interpret that result several times
161 return stat
.S_ISBLK(stat_obj
)
164 def _lsblk_parser(line
):
166 Parses lines in lsblk output. Requires output to be in pair mode (``-P`` flag). Lines
167 need to be whole strings, the line gets split when processed.
169 :param line: A string, with the full line from lsblk output
171 # parse the COLUMN="value" output to construct the dictionary
172 pairs
= line
.split('" ')
176 column
, value
= pair
.split('=')
179 parsed
[column
] = value
.strip().strip().strip('"')
183 def device_family(device
):
185 Returns a list of associated devices. It assumes that ``device`` is
186 a parent device. It is up to the caller to ensure that the device being
187 used is a parent, not a partition.
189 labels
= ['NAME', 'PARTLABEL', 'TYPE']
190 command
= ['lsblk', '-P', '-p', '-o', ','.join(labels
), device
]
191 out
, err
, rc
= process
.call(command
)
194 devices
.append(_lsblk_parser(line
))
199 def udevadm_property(device
, properties
=[]):
201 Query udevadm for information about device properties.
202 Optionally pass a list of properties to return. A requested property might
203 not be returned if not present.
205 Expected output format::
206 # udevadm info --query=property --name=/dev/sda :(
211 ID_MODEL=SK_hynix_SC311_SATA_512GB
212 ID_PART_TABLE_TYPE=gpt
213 ID_PART_TABLE_UUID=c8f91d57-b26c-4de1-8884-0c9541da288c
214 ID_PATH=pci-0000:00:17.0-ata-3
215 ID_PATH_TAG=pci-0000_00_17_0-ata-3
217 ID_SERIAL=SK_hynix_SC311_SATA_512GB_MS83N71801150416A
219 USEC_INITIALIZED=16117769
222 out
= _udevadm_info(device
)
225 p
, v
= line
.split('=', 1)
226 if not properties
or p
in properties
:
231 def _udevadm_info(device
):
233 Call udevadm and return the output
235 cmd
= ['udevadm', 'info', '--query=property', device
]
236 out
, _err
, _rc
= process
.call(cmd
)
240 def lsblk(device
, columns
=None, abspath
=False):
242 if not os
.path
.isdir(device
):
243 result
= lsblk_all(device
=device
,
247 logger
.debug(f
"{device} not found is lsblk report")
252 def lsblk_all(device
='', columns
=None, abspath
=False):
254 Create a dictionary of identifying values for a device using ``lsblk``.
255 Each supported column is a key, in its *raw* format (all uppercase
256 usually). ``lsblk`` has support for certain "columns" (in blkid these
257 would be labels), and these columns vary between distributions and
258 ``lsblk`` versions. The newer versions support a richer set of columns,
259 while older ones were a bit limited.
261 These are a subset of lsblk columns which are known to work on both CentOS 7 and Xenial:
264 KNAME internal kernel device name
265 PKNAME internal kernel parent device name
266 MAJ:MIN major:minor device number
267 FSTYPE filesystem type
268 MOUNTPOINT where the device is mounted
269 LABEL filesystem LABEL
273 MODEL device identifier
274 SIZE size of the device
275 STATE state of the device
278 MODE device node permissions
279 ALIGNMENT alignment offset
280 MIN-IO minimum I/O size
281 OPT-IO optimal I/O size
282 PHY-SEC physical sector size
283 LOG-SEC logical sector size
284 ROTA rotational device
285 SCHED I/O scheduler name
286 RQ-SIZE request queue size
288 PKNAME internal parent kernel device name
289 DISC-ALN discard alignment offset
290 DISC-GRAN discard granularity
291 DISC-MAX discard max bytes
292 DISC-ZERO discard zeroes data
294 There is a bug in ``lsblk`` where using all the available (supported)
295 columns will result in no output (!), in order to workaround this the
296 following columns have been removed from the default reporting columns:
298 * RQ-SIZE (request queue size)
299 * MIN-IO minimum I/O size
300 * OPT-IO optimal I/O size
302 These should be available however when using `columns`. For example::
304 >>> lsblk('/dev/sda1', columns=['OPT-IO'])
307 Normal CLI output, as filtered by the flags in this function will look like ::
309 $ lsblk -P -o NAME,KNAME,PKNAME,MAJ:MIN,FSTYPE,MOUNTPOINT
310 NAME="sda1" KNAME="sda1" MAJ:MIN="8:1" FSTYPE="ext4" MOUNTPOINT="/"
312 :param columns: A list of columns to report as keys in its original form.
313 :param abspath: Set the flag for absolute paths on the report
316 'NAME', 'KNAME', 'PKNAME', 'MAJ:MIN', 'FSTYPE', 'MOUNTPOINT', 'LABEL',
317 'UUID', 'RO', 'RM', 'MODEL', 'SIZE', 'STATE', 'OWNER', 'GROUP', 'MODE',
318 'ALIGNMENT', 'PHY-SEC', 'LOG-SEC', 'ROTA', 'SCHED', 'TYPE', 'DISC-ALN',
319 'DISC-GRAN', 'DISC-MAX', 'DISC-ZERO', 'PKNAME', 'PARTLABEL'
321 columns
= columns
or default_columns
322 # -P -> Produce pairs of COLUMN="value"
323 # -p -> Return full paths to devices, not just the names, when ``abspath`` is set
324 # -o -> Use the columns specified or default ones provided by this function
325 base_command
= ['lsblk', '-P']
327 base_command
.append('-p')
328 base_command
.append('-o')
329 base_command
.append(','.join(columns
))
331 base_command
.append('--nodeps')
332 base_command
.append(device
)
334 out
, err
, rc
= process
.call(base_command
)
337 raise RuntimeError(f
"Error: {err}")
342 result
.append(_lsblk_parser(line
))
349 Boolean to determine if a given device is a block device (**not**
352 For example: /dev/sda would return True, but not /dev/sdc1
354 if not os
.path
.exists(dev
):
356 if not dev
.startswith('/dev/'):
358 if dev
[len('/dev/'):].startswith('loop'):
359 if not allow_loop_devices():
363 return _stat_is_device(os
.lstat(dev
).st_mode
)
366 def is_partition(dev
):
368 Boolean to determine if a given device is a partition, like /dev/sda1
370 if not os
.path
.exists(dev
):
372 # use lsblk first, fall back to using stat
373 TYPE
= lsblk(dev
).get('TYPE')
375 return TYPE
== 'part'
378 stat_obj
= os
.stat(dev
)
379 if _stat_is_device(stat_obj
.st_mode
):
382 major
= os
.major(stat_obj
.st_rdev
)
383 minor
= os
.minor(stat_obj
.st_rdev
)
384 if os
.path
.exists('/sys/dev/block/%d:%d/partition' % (major
, minor
)):
389 def is_ceph_rbd(dev
):
391 Boolean to determine if a given device is a ceph RBD device, like /dev/rbd0
393 return dev
.startswith(('/dev/rbd'))
396 class BaseFloatUnit(float):
398 Base class to support float representations of size values. Suffix is
399 computed on child classes by inspecting the class name
403 return "<%s(%s)>" % (self
.__class
__.__name
__, self
.__float
__())
406 return "{size:.2f} {suffix}".format(
407 size
=self
.__float
__(),
408 suffix
=self
.__class
__.__name
__.split('Float')[-1]
412 return int(self
.real
)
418 class FloatB(BaseFloatUnit
):
422 class FloatMB(BaseFloatUnit
):
426 class FloatGB(BaseFloatUnit
):
430 class FloatKB(BaseFloatUnit
):
434 class FloatTB(BaseFloatUnit
):
437 class FloatPB(BaseFloatUnit
):
442 Helper to provide an interface for different sizes given a single initial
443 input. Allows for comparison between different size objects, which avoids
444 the need to convert sizes before comparison (e.g. comparing megabytes
447 Common comparison operators are supported::
449 >>> hd1 = Size(gb=400)
450 >>> hd2 = Size(gb=500)
457 >>> hd1 == Size(gb=400)
460 The Size object can also be multiplied or divided::
469 Additions and subtractions are only supported between Size objects::
471 >>> Size(gb=224) - Size(gb=100)
473 >>> Size(gb=1) + Size(mb=300)
476 Can also display a human-readable representation, with automatic detection
477 on best suited unit, or alternatively, specific unit representation::
479 >>> s = Size(mb=2211)
484 >>> print("Total size: %s" % s.mb)
485 Total size: 2211.00 MB
486 >>> print("Total size: %s" % s)
491 def parse(cls
, size
):
492 if (len(size
) > 2 and
493 size
[-2].lower() in ['k', 'm', 'g', 't', 'p'] and
494 size
[-1].lower() == 'b'):
495 return cls(**{size
[-2:].lower(): float(size
[0:-2])})
496 elif size
[-1].lower() in ['b', 'k', 'm', 'g', 't', 'p']:
497 return cls(**{size
[-1].lower(): float(size
[0:-1])})
499 return cls(b
=float(size
))
502 def __init__(self
, multiplier
=1024, **kw
):
503 self
._multiplier
= multiplier
504 # create a mapping of units-to-multiplier, skip bytes as that is
505 # calculated initially always and does not need to convert
507 [('k', 'kb', 'kilobytes'), self
._multiplier
],
508 [('m', 'mb', 'megabytes'), self
._multiplier
** 2],
509 [('g', 'gb', 'gigabytes'), self
._multiplier
** 3],
510 [('t', 'tb', 'terabytes'), self
._multiplier
** 4],
511 [('p', 'pb', 'petabytes'), self
._multiplier
** 5]
513 # and mappings for units-to-formatters, including bytes and aliases for
516 [('b', 'bytes'), FloatB
],
517 [('kb', 'kilobytes'), FloatKB
],
518 [('mb', 'megabytes'), FloatMB
],
519 [('gb', 'gigabytes'), FloatGB
],
520 [('tb', 'terabytes'), FloatTB
],
521 [('pb', 'petabytes'), FloatPB
],
523 self
._formatters
= {}
524 for key
, value
in format_aliases
:
526 self
._formatters
[alias
] = value
528 for key
, value
in aliases
:
530 self
._factors
[alias
] = value
532 for k
, v
in kw
.items():
534 # only pursue the first occurrence
537 def _convert(self
, size
, unit
):
539 Convert any size down to bytes so that other methods can rely on bytes
540 being available always, regardless of what they pass in, avoiding the
541 need for a mapping of every permutation.
543 if unit
in ['b', 'bytes']:
546 factor
= self
._factors
[unit
]
547 self
._b
= float(size
* factor
)
549 def _get_best_format(self
):
551 Go through all the supported units, and use the first one that is less
552 than 1024. This allows to represent size in the most readable format
555 for unit
in ['b', 'kb', 'mb', 'gb', 'tb', 'pb']:
556 if getattr(self
, unit
) > 1024:
558 return getattr(self
, unit
)
561 return "<Size(%s)>" % self
._get
_best
_format
()
564 return "%s" % self
._get
_best
_format
()
566 def __format__(self
, spec
):
567 return str(self
._get
_best
_format
()).__format
__(spec
)
575 def __lt__(self
, other
):
576 if isinstance(other
, Size
):
577 return self
._b
< other
._b
579 return self
.b
< other
581 def __le__(self
, other
):
582 if isinstance(other
, Size
):
583 return self
._b
<= other
._b
585 return self
.b
<= other
587 def __eq__(self
, other
):
588 if isinstance(other
, Size
):
589 return self
._b
== other
._b
591 return self
.b
== other
593 def __ne__(self
, other
):
594 if isinstance(other
, Size
):
595 return self
._b
!= other
._b
597 return self
.b
!= other
599 def __ge__(self
, other
):
600 if isinstance(other
, Size
):
601 return self
._b
>= other
._b
603 return self
.b
>= other
605 def __gt__(self
, other
):
606 if isinstance(other
, Size
):
607 return self
._b
> other
._b
609 return self
.b
> other
611 def __add__(self
, other
):
612 if isinstance(other
, Size
):
613 _b
= self
._b
+ other
._b
615 raise TypeError('Cannot add "Size" object with int')
617 def __sub__(self
, other
):
618 if isinstance(other
, Size
):
619 _b
= self
._b
- other
._b
621 raise TypeError('Cannot subtract "Size" object from int')
623 def __mul__(self
, other
):
624 if isinstance(other
, Size
):
625 raise TypeError('Cannot multiply with "Size" object')
629 def __truediv__(self
, other
):
630 if isinstance(other
, Size
):
631 return self
._b
/ other
._b
635 def __div__(self
, other
):
636 if isinstance(other
, Size
):
637 return self
._b
/ other
._b
644 def __nonzero__(self
):
645 return self
.__bool
__()
647 def __getattr__(self
, unit
):
649 Calculate units on the fly, relies on the fact that ``bytes`` has been
650 converted at instantiation. Units that don't exist will trigger an
654 formatter
= self
._formatters
[unit
]
656 raise AttributeError('Size object has not attribute "%s"' % unit
)
657 if unit
in ['b', 'bytes']:
658 return formatter(self
._b
)
660 factor
= self
._factors
[unit
]
662 raise AttributeError('Size object has not attribute "%s"' % unit
)
663 return formatter(float(self
._b
) / factor
)
666 def human_readable_size(size
):
668 Take a size in bytes, and transform it into a human readable size with up
669 to two decimals of precision.
671 suffixes
= ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
672 for suffix
in suffixes
:
677 return "{size:.2f} {suffix}".format(
682 def size_from_human_readable(s
):
684 Takes a human readable string and converts into a Size. If no unit is
685 passed, bytes is assumed.
687 s
= s
.replace(' ', '')
689 return Size(b
=float(s
))
691 if s
[-1].lower() == 'p':
693 if s
[-1].lower() == 't':
695 if s
[-1].lower() == 'g':
697 if s
[-1].lower() == 'm':
699 if s
[-1].lower() == 'k':
704 def get_partitions_facts(sys_block_path
):
705 partition_metadata
= {}
706 for folder
in os
.listdir(sys_block_path
):
707 folder_path
= os
.path
.join(sys_block_path
, folder
)
708 if os
.path
.exists(os
.path
.join(folder_path
, 'partition')):
709 contents
= get_file_contents(os
.path
.join(folder_path
, 'partition'))
713 part_sys_block_path
= os
.path
.join(sys_block_path
, partname
)
715 part
['start'] = get_file_contents(part_sys_block_path
+ "/start", 0)
716 part
['sectors'] = get_file_contents(part_sys_block_path
+ "/size", 0)
718 part
['sectorsize'] = get_file_contents(
719 part_sys_block_path
+ "/queue/logical_block_size")
720 if not part
['sectorsize']:
721 part
['sectorsize'] = get_file_contents(
722 part_sys_block_path
+ "/queue/hw_sector_size", 512)
723 part
['size'] = float(part
['sectors']) * 512
724 part
['human_readable_size'] = human_readable_size(float(part
['sectors']) * 512)
726 for holder
in os
.listdir(part_sys_block_path
+ '/holders'):
727 part
['holders'].append(holder
)
729 partition_metadata
[partname
] = part
730 return partition_metadata
733 def is_mapper_device(device_name
):
734 return device_name
.startswith(('/dev/mapper', '/dev/dm-'))
737 def is_locked_raw_device(disk_path
):
739 A device can be locked by a third party software like a database.
740 To detect that case, the device is opened in Read/Write and exclusive mode
742 open_flags
= (os
.O_RDWR | os
.O_EXCL
)
747 fd
= os
.open(disk_path
, open_flags
, open_mode
)
759 class AllowLoopDevices(object):
765 val
= os
.environ
.get("CEPH_VOLUME_ALLOW_LOOP_DEVICES", "false").lower()
766 if val
not in ("false", 'no', '0'):
770 "CEPH_VOLUME_ALLOW_LOOP_DEVICES is set in your "
771 "environment, so we will allow the use of unattached loop"
772 " devices as disks. This feature is intended for "
773 "development purposes only and will never be supported in"
774 " production. Issues filed based on this behavior will "
781 allow_loop_devices
= AllowLoopDevices()
784 def get_block_devs_sysfs(_sys_block_path
='/sys/block', _sys_dev_block_path
='/sys/dev/block'):
785 def holder_inner_loop():
786 for holder
in holders
:
787 # /sys/block/sdy/holders/dm-8/dm/uuid
788 holder_dm_type
= get_file_contents(os
.path
.join(_sys_block_path
, dev
, f
'holders/{holder}/dm/uuid')).split('-')[0].lower()
789 if holder_dm_type
== 'mpath':
792 # First, get devices that are _not_ partitions
794 dev_names
= os
.listdir(_sys_block_path
)
795 for dev
in dev_names
:
796 name
= kname
= os
.path
.join("/dev", dev
)
797 if not os
.path
.exists(name
):
800 holders
= os
.listdir(os
.path
.join(_sys_block_path
, dev
, 'holders'))
801 if get_file_contents(os
.path
.join(_sys_block_path
, dev
, 'removable')) == "1":
803 if holder_inner_loop():
805 dm_dir_path
= os
.path
.join(_sys_block_path
, dev
, 'dm')
806 if os
.path
.isdir(dm_dir_path
):
807 dm_type
= get_file_contents(os
.path
.join(dm_dir_path
, 'uuid'))
808 type_
= dm_type
.split('-')[0].lower()
809 basename
= get_file_contents(os
.path
.join(dm_dir_path
, 'name'))
810 name
= os
.path
.join("/dev/mapper", basename
)
811 if dev
.startswith('loop'):
812 if not allow_loop_devices():
814 # Skip loop devices that are not attached
815 if not os
.path
.exists(os
.path
.join(_sys_block_path
, dev
, 'loop')):
818 result
.append([kname
, name
, type_
])
819 # Next, look for devices that _are_ partitions
820 for item
in os
.listdir(_sys_dev_block_path
):
821 is_part
= get_file_contents(os
.path
.join(_sys_dev_block_path
, item
, 'partition')) == "1"
822 dev
= os
.path
.basename(os
.readlink(os
.path
.join(_sys_dev_block_path
, item
)))
825 name
= kname
= os
.path
.join("/dev", dev
)
826 result
.append([name
, kname
, "part"])
827 return sorted(result
, key
=lambda x
: x
[0])
830 def get_devices(_sys_block_path
='/sys/block', device
=''):
832 Captures all available block devices as reported by lsblk.
833 Additional interesting metadata like sectors, size, vendor,
834 solid/rotational, etc. is collected from /sys/block/<device>
836 Returns a dictionary, where keys are the full paths to devices.
838 ..note:: loop devices, removable media, and logical volumes are never included.
843 block_devs
= get_block_devs_sysfs(_sys_block_path
)
845 block_types
= ['disk', 'mpath']
846 if allow_loop_devices():
847 block_types
.append('loop')
849 for block
in block_devs
:
850 devname
= os
.path
.basename(block
[0])
852 if block
[2] not in block_types
:
854 sysdir
= os
.path
.join(_sys_block_path
, devname
)
857 # If the device is ceph rbd it gets excluded
858 if is_ceph_rbd(diskname
):
861 # If the mapper device is a logical volume it gets excluded
862 if is_mapper_device(diskname
):
863 if lvm
.get_device_lvs(diskname
):
866 # all facts that have no defaults
867 # (<name>, <path relative to _sys_block_path>)
868 facts
= [('removable', 'removable'),
870 ('vendor', 'device/vendor'),
871 ('model', 'device/model'),
872 ('rev', 'device/rev'),
873 ('sas_address', 'device/sas_address'),
874 ('sas_device_handle', 'device/sas_device_handle'),
875 ('support_discard', 'queue/discard_granularity'),
876 ('rotational', 'queue/rotational'),
877 ('nr_requests', 'queue/nr_requests'),
879 for key
, file_
in facts
:
880 metadata
[key
] = get_file_contents(os
.path
.join(sysdir
, file_
))
882 device_slaves
= os
.listdir(os
.path
.join(sysdir
, 'slaves'))
884 metadata
['device_nodes'] = ','.join(device_slaves
)
886 metadata
['device_nodes'] = devname
888 metadata
['scheduler_mode'] = ""
889 scheduler
= get_file_contents(sysdir
+ "/queue/scheduler")
890 if scheduler
is not None:
891 m
= re
.match(r
".*?(\[(.*)\])", scheduler
)
893 metadata
['scheduler_mode'] = m
.group(2)
895 metadata
['partitions'] = get_partitions_facts(sysdir
)
897 size
= get_file_contents(os
.path
.join(sysdir
, 'size'), 0)
899 metadata
['sectors'] = get_file_contents(os
.path
.join(sysdir
, 'sectors'), 0)
900 fallback_sectorsize
= get_file_contents(sysdir
+ "/queue/hw_sector_size", 512)
901 metadata
['sectorsize'] = get_file_contents(sysdir
+
902 "/queue/logical_block_size",
904 metadata
['size'] = float(size
) * 512
905 metadata
['human_readable_size'] = human_readable_size(metadata
['size'])
906 metadata
['path'] = diskname
907 metadata
['locked'] = is_locked_raw_device(metadata
['path'])
908 metadata
['type'] = block
[2]
910 device_facts
[diskname
] = metadata
913 def has_bluestore_label(device_path
):
915 bluestoreDiskSignature
= 'bluestore block device' # 22 bytes long
917 # throws OSError on failure
918 logger
.info("opening device {} to check for BlueStore label".format(device_path
))
920 with
open(device_path
, "rb") as fd
:
921 # read first 22 bytes looking for bluestore disk signature
922 signature
= fd
.read(22)
923 if signature
.decode('ascii', 'replace') == bluestoreDiskSignature
:
925 except IsADirectoryError
:
926 logger
.info(f
'{device_path} is a directory, skipping.')