]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/disk.py
d2459e1208674d6b49e63348b29138f4350d315e
5 from ceph_volume
import process
6 from ceph_volume
.api
import lvm
7 from ceph_volume
.util
.system
import get_file_contents
10 logger
= logging
.getLogger(__name__
)
13 # The blkid CLI tool has some oddities which prevents having one common call
14 # to extract the information instead of having separate utilities. The `udev`
15 # type of output is needed in older versions of blkid (v 2.23) that will not
16 # work correctly with just the ``-p`` flag to bypass the cache for example.
17 # Xenial doesn't have this problem as it uses a newer blkid version.
20 def get_partuuid(device
):
22 If a device is a partition, it will probably have a PARTUUID on it that
23 will persist and can be queried against `blkid` later to detect the actual
26 out
, err
, rc
= process
.call(
27 ['blkid', '-c', '/dev/null', '-s', 'PARTUUID', '-o', 'value', device
]
29 return ' '.join(out
).strip()
32 def _blkid_parser(output
):
34 Parses the output from a system ``blkid`` call, requires output to be
35 produced using the ``-p`` flag which bypasses the cache, mangling the
36 names. These names are corrected to what it would look like without the
41 /dev/sdb1: UUID="62416664-cbaf-40bd-9689-10bd337379c3" TYPE="xfs" [...]
43 # first spaced separated item is garbage, gets tossed:
44 output
= ' '.join(output
.split()[1:])
45 # split again, respecting possible whitespace in quoted values
46 pairs
= output
.split('" ')
52 'PART_ENTRY_NAME': 'PARTLABEL',
53 'PART_ENTRY_UUID': 'PARTUUID',
54 'PART_ENTRY_TYPE': 'PARTTYPE',
60 column
, value
= pair
.split('=')
63 raw
[column
] = value
.strip().strip().strip('"')
65 for key
, value
in raw
.items():
66 new_key
= mapping
.get(key
)
69 processed
[new_key
] = value
76 The blkid interface to its CLI, creating an output similar to what is
77 expected from ``lsblk``. In most cases, ``lsblk()`` should be the preferred
78 method for extracting information about a device. There are some corner
79 cases where it might provide information that is otherwise unavailable.
81 The system call uses the ``-p`` flag which bypasses the cache, the caveat
82 being that the keys produced are named completely different to expected
85 For example, instead of ``PARTLABEL`` it provides a ``PART_ENTRY_NAME``.
86 A bit of translation between these known keys is done, which is why
87 ``lsblk`` should always be preferred: the output provided here is not as
88 rich, given that a translation of keys is required for a uniform interface
91 Label name to expected output chart:
93 cache bypass name expected name
97 PART_ENTRY_NAME PARTLABEL
98 PART_ENTRY_UUID PARTUUID
100 out
, err
, rc
= process
.call(
101 ['blkid', '-c', '/dev/null', '-p', device
]
103 return _blkid_parser(' '.join(out
))
106 def get_part_entry_type(device
):
108 Parses the ``ID_PART_ENTRY_TYPE`` from the "low level" (bypasses the cache)
109 output that uses the ``udev`` type of output. This output is intended to be
110 used for udev rules, but it is useful in this case as it is the only
111 consistent way to retrieve the GUID used by ceph-disk to identify devices.
113 out
, err
, rc
= process
.call(['blkid', '-c', '/dev/null', '-p', '-o', 'udev', device
])
115 if 'ID_PART_ENTRY_TYPE=' in line
:
116 return line
.split('=')[-1].strip()
120 def get_device_from_partuuid(partuuid
):
122 If a device has a partuuid, query blkid so that it can tell us what that
125 out
, err
, rc
= process
.call(
126 ['blkid', '-c', '/dev/null', '-t', 'PARTUUID="%s"' % partuuid
, '-o', 'device']
128 return ' '.join(out
).strip()
131 def remove_partition(device
):
133 Removes a partition using parted
135 :param device: A ``Device()`` object
137 udev_info
= udevadm_property(device
.path
)
138 partition_number
= udev_info
.get('ID_PART_ENTRY_NUMBER')
139 if not partition_number
:
140 raise RuntimeError('Unable to detect the partition number for device: %s' % device
.path
)
143 ['parted', device
.parent_device
, '--script', '--', 'rm', partition_number
]
147 def _stat_is_device(stat_obj
):
149 Helper function that will interpret ``os.stat`` output directly, so that other
150 functions can call ``os.stat`` once and interpret that result several times
152 return stat
.S_ISBLK(stat_obj
)
155 def _lsblk_parser(line
):
157 Parses lines in lsblk output. Requires output to be in pair mode (``-P`` flag). Lines
158 need to be whole strings, the line gets split when processed.
160 :param line: A string, with the full line from lsblk output
162 # parse the COLUMN="value" output to construct the dictionary
163 pairs
= line
.split('" ')
167 column
, value
= pair
.split('=')
170 parsed
[column
] = value
.strip().strip().strip('"')
174 def device_family(device
):
176 Returns a list of associated devices. It assumes that ``device`` is
177 a parent device. It is up to the caller to ensure that the device being
178 used is a parent, not a partition.
180 labels
= ['NAME', 'PARTLABEL', 'TYPE']
181 command
= ['lsblk', '-P', '-p', '-o', ','.join(labels
), device
]
182 out
, err
, rc
= process
.call(command
)
185 devices
.append(_lsblk_parser(line
))
190 def udevadm_property(device
, properties
=[]):
192 Query udevadm for information about device properties.
193 Optionally pass a list of properties to return. A requested property might
194 not be returned if not present.
196 Expected output format::
197 # udevadm info --query=property --name=/dev/sda :(
202 ID_MODEL=SK_hynix_SC311_SATA_512GB
203 ID_PART_TABLE_TYPE=gpt
204 ID_PART_TABLE_UUID=c8f91d57-b26c-4de1-8884-0c9541da288c
205 ID_PATH=pci-0000:00:17.0-ata-3
206 ID_PATH_TAG=pci-0000_00_17_0-ata-3
208 ID_SERIAL=SK_hynix_SC311_SATA_512GB_MS83N71801150416A
210 USEC_INITIALIZED=16117769
213 out
= _udevadm_info(device
)
216 p
, v
= line
.split('=', 1)
217 if not properties
or p
in properties
:
222 def _udevadm_info(device
):
224 Call udevadm and return the output
226 cmd
= ['udevadm', 'info', '--query=property', device
]
227 out
, _err
, _rc
= process
.call(cmd
)
231 def lsblk(device
, columns
=None, abspath
=False):
232 return lsblk_all(device
=device
,
236 def lsblk_all(device
='', columns
=None, abspath
=False):
238 Create a dictionary of identifying values for a device using ``lsblk``.
239 Each supported column is a key, in its *raw* format (all uppercase
240 usually). ``lsblk`` has support for certain "columns" (in blkid these
241 would be labels), and these columns vary between distributions and
242 ``lsblk`` versions. The newer versions support a richer set of columns,
243 while older ones were a bit limited.
245 These are a subset of lsblk columns which are known to work on both CentOS 7 and Xenial:
248 KNAME internal kernel device name
249 PKNAME internal kernel parent device name
250 MAJ:MIN major:minor device number
251 FSTYPE filesystem type
252 MOUNTPOINT where the device is mounted
253 LABEL filesystem LABEL
257 MODEL device identifier
258 SIZE size of the device
259 STATE state of the device
262 MODE device node permissions
263 ALIGNMENT alignment offset
264 MIN-IO minimum I/O size
265 OPT-IO optimal I/O size
266 PHY-SEC physical sector size
267 LOG-SEC logical sector size
268 ROTA rotational device
269 SCHED I/O scheduler name
270 RQ-SIZE request queue size
272 PKNAME internal parent kernel device name
273 DISC-ALN discard alignment offset
274 DISC-GRAN discard granularity
275 DISC-MAX discard max bytes
276 DISC-ZERO discard zeroes data
278 There is a bug in ``lsblk`` where using all the available (supported)
279 columns will result in no output (!), in order to workaround this the
280 following columns have been removed from the default reporting columns:
282 * RQ-SIZE (request queue size)
283 * MIN-IO minimum I/O size
284 * OPT-IO optimal I/O size
286 These should be available however when using `columns`. For example::
288 >>> lsblk('/dev/sda1', columns=['OPT-IO'])
291 Normal CLI output, as filtered by the flags in this function will look like ::
293 $ lsblk -P -o NAME,KNAME,PKNAME,MAJ:MIN,FSTYPE,MOUNTPOINT
294 NAME="sda1" KNAME="sda1" MAJ:MIN="8:1" FSTYPE="ext4" MOUNTPOINT="/"
296 :param columns: A list of columns to report as keys in its original form.
297 :param abspath: Set the flag for absolute paths on the report
300 'NAME', 'KNAME', 'PKNAME', 'MAJ:MIN', 'FSTYPE', 'MOUNTPOINT', 'LABEL',
301 'UUID', 'RO', 'RM', 'MODEL', 'SIZE', 'STATE', 'OWNER', 'GROUP', 'MODE',
302 'ALIGNMENT', 'PHY-SEC', 'LOG-SEC', 'ROTA', 'SCHED', 'TYPE', 'DISC-ALN',
303 'DISC-GRAN', 'DISC-MAX', 'DISC-ZERO', 'PKNAME', 'PARTLABEL'
305 columns
= columns
or default_columns
306 # -P -> Produce pairs of COLUMN="value"
307 # -p -> Return full paths to devices, not just the names, when ``abspath`` is set
308 # -o -> Use the columns specified or default ones provided by this function
309 base_command
= ['lsblk', '-P']
311 base_command
.append('-p')
312 base_command
.append('-o')
313 base_command
.append(','.join(columns
))
315 out
, err
, rc
= process
.call(base_command
)
318 raise RuntimeError(f
"Error: {err}")
323 result
.append(_lsblk_parser(line
))
329 if dev
['NAME'] == os
.path
.basename(device
):
336 Boolean to determine if a given device is a block device (**not**
339 For example: /dev/sda would return True, but not /dev/sdc1
341 if not os
.path
.exists(dev
):
343 if not dev
.startswith('/dev/'):
345 if dev
[len('/dev/'):].startswith('loop'):
346 if not allow_loop_devices():
350 return _stat_is_device(os
.lstat(dev
).st_mode
)
353 def is_partition(dev
):
355 Boolean to determine if a given device is a partition, like /dev/sda1
357 if not os
.path
.exists(dev
):
359 # use lsblk first, fall back to using stat
360 TYPE
= lsblk(dev
).get('TYPE')
362 return TYPE
== 'part'
365 stat_obj
= os
.stat(dev
)
366 if _stat_is_device(stat_obj
.st_mode
):
369 major
= os
.major(stat_obj
.st_rdev
)
370 minor
= os
.minor(stat_obj
.st_rdev
)
371 if os
.path
.exists('/sys/dev/block/%d:%d/partition' % (major
, minor
)):
376 def is_ceph_rbd(dev
):
378 Boolean to determine if a given device is a ceph RBD device, like /dev/rbd0
380 return dev
.startswith(('/dev/rbd'))
383 class BaseFloatUnit(float):
385 Base class to support float representations of size values. Suffix is
386 computed on child classes by inspecting the class name
390 return "<%s(%s)>" % (self
.__class
__.__name
__, self
.__float
__())
393 return "{size:.2f} {suffix}".format(
394 size
=self
.__float
__(),
395 suffix
=self
.__class
__.__name
__.split('Float')[-1]
399 return int(self
.real
)
405 class FloatB(BaseFloatUnit
):
409 class FloatMB(BaseFloatUnit
):
413 class FloatGB(BaseFloatUnit
):
417 class FloatKB(BaseFloatUnit
):
421 class FloatTB(BaseFloatUnit
):
424 class FloatPB(BaseFloatUnit
):
429 Helper to provide an interface for different sizes given a single initial
430 input. Allows for comparison between different size objects, which avoids
431 the need to convert sizes before comparison (e.g. comparing megabytes
434 Common comparison operators are supported::
436 >>> hd1 = Size(gb=400)
437 >>> hd2 = Size(gb=500)
444 >>> hd1 == Size(gb=400)
447 The Size object can also be multiplied or divided::
456 Additions and subtractions are only supported between Size objects::
458 >>> Size(gb=224) - Size(gb=100)
460 >>> Size(gb=1) + Size(mb=300)
463 Can also display a human-readable representation, with automatic detection
464 on best suited unit, or alternatively, specific unit representation::
466 >>> s = Size(mb=2211)
471 >>> print("Total size: %s" % s.mb)
472 Total size: 2211.00 MB
473 >>> print("Total size: %s" % s)
478 def parse(cls
, size
):
479 if (len(size
) > 2 and
480 size
[-2].lower() in ['k', 'm', 'g', 't', 'p'] and
481 size
[-1].lower() == 'b'):
482 return cls(**{size
[-2:].lower(): float(size
[0:-2])})
483 elif size
[-1].lower() in ['b', 'k', 'm', 'g', 't', 'p']:
484 return cls(**{size
[-1].lower(): float(size
[0:-1])})
486 return cls(b
=float(size
))
489 def __init__(self
, multiplier
=1024, **kw
):
490 self
._multiplier
= multiplier
491 # create a mapping of units-to-multiplier, skip bytes as that is
492 # calculated initially always and does not need to convert
494 [('k', 'kb', 'kilobytes'), self
._multiplier
],
495 [('m', 'mb', 'megabytes'), self
._multiplier
** 2],
496 [('g', 'gb', 'gigabytes'), self
._multiplier
** 3],
497 [('t', 'tb', 'terabytes'), self
._multiplier
** 4],
498 [('p', 'pb', 'petabytes'), self
._multiplier
** 5]
500 # and mappings for units-to-formatters, including bytes and aliases for
503 [('b', 'bytes'), FloatB
],
504 [('kb', 'kilobytes'), FloatKB
],
505 [('mb', 'megabytes'), FloatMB
],
506 [('gb', 'gigabytes'), FloatGB
],
507 [('tb', 'terabytes'), FloatTB
],
508 [('pb', 'petabytes'), FloatPB
],
510 self
._formatters
= {}
511 for key
, value
in format_aliases
:
513 self
._formatters
[alias
] = value
515 for key
, value
in aliases
:
517 self
._factors
[alias
] = value
519 for k
, v
in kw
.items():
521 # only pursue the first occurrence
524 def _convert(self
, size
, unit
):
526 Convert any size down to bytes so that other methods can rely on bytes
527 being available always, regardless of what they pass in, avoiding the
528 need for a mapping of every permutation.
530 if unit
in ['b', 'bytes']:
533 factor
= self
._factors
[unit
]
534 self
._b
= float(size
* factor
)
536 def _get_best_format(self
):
538 Go through all the supported units, and use the first one that is less
539 than 1024. This allows to represent size in the most readable format
542 for unit
in ['b', 'kb', 'mb', 'gb', 'tb', 'pb']:
543 if getattr(self
, unit
) > 1024:
545 return getattr(self
, unit
)
548 return "<Size(%s)>" % self
._get
_best
_format
()
551 return "%s" % self
._get
_best
_format
()
553 def __format__(self
, spec
):
554 return str(self
._get
_best
_format
()).__format
__(spec
)
562 def __lt__(self
, other
):
563 if isinstance(other
, Size
):
564 return self
._b
< other
._b
566 return self
.b
< other
568 def __le__(self
, other
):
569 if isinstance(other
, Size
):
570 return self
._b
<= other
._b
572 return self
.b
<= other
574 def __eq__(self
, other
):
575 if isinstance(other
, Size
):
576 return self
._b
== other
._b
578 return self
.b
== other
580 def __ne__(self
, other
):
581 if isinstance(other
, Size
):
582 return self
._b
!= other
._b
584 return self
.b
!= other
586 def __ge__(self
, other
):
587 if isinstance(other
, Size
):
588 return self
._b
>= other
._b
590 return self
.b
>= other
592 def __gt__(self
, other
):
593 if isinstance(other
, Size
):
594 return self
._b
> other
._b
596 return self
.b
> other
598 def __add__(self
, other
):
599 if isinstance(other
, Size
):
600 _b
= self
._b
+ other
._b
602 raise TypeError('Cannot add "Size" object with int')
604 def __sub__(self
, other
):
605 if isinstance(other
, Size
):
606 _b
= self
._b
- other
._b
608 raise TypeError('Cannot subtract "Size" object from int')
610 def __mul__(self
, other
):
611 if isinstance(other
, Size
):
612 raise TypeError('Cannot multiply with "Size" object')
616 def __truediv__(self
, other
):
617 if isinstance(other
, Size
):
618 return self
._b
/ other
._b
622 def __div__(self
, other
):
623 if isinstance(other
, Size
):
624 return self
._b
/ other
._b
631 def __nonzero__(self
):
632 return self
.__bool
__()
634 def __getattr__(self
, unit
):
636 Calculate units on the fly, relies on the fact that ``bytes`` has been
637 converted at instantiation. Units that don't exist will trigger an
641 formatter
= self
._formatters
[unit
]
643 raise AttributeError('Size object has not attribute "%s"' % unit
)
644 if unit
in ['b', 'bytes']:
645 return formatter(self
._b
)
647 factor
= self
._factors
[unit
]
649 raise AttributeError('Size object has not attribute "%s"' % unit
)
650 return formatter(float(self
._b
) / factor
)
653 def human_readable_size(size
):
655 Take a size in bytes, and transform it into a human readable size with up
656 to two decimals of precision.
658 suffixes
= ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
659 for suffix
in suffixes
:
664 return "{size:.2f} {suffix}".format(
669 def size_from_human_readable(s
):
671 Takes a human readable string and converts into a Size. If no unit is
672 passed, bytes is assumed.
674 s
= s
.replace(' ', '')
676 return Size(b
=float(s
))
678 if s
[-1].lower() == 'p':
680 if s
[-1].lower() == 't':
682 if s
[-1].lower() == 'g':
684 if s
[-1].lower() == 'm':
686 if s
[-1].lower() == 'k':
691 def get_partitions_facts(sys_block_path
):
692 partition_metadata
= {}
693 for folder
in os
.listdir(sys_block_path
):
694 folder_path
= os
.path
.join(sys_block_path
, folder
)
695 if os
.path
.exists(os
.path
.join(folder_path
, 'partition')):
696 contents
= get_file_contents(os
.path
.join(folder_path
, 'partition'))
700 part_sys_block_path
= os
.path
.join(sys_block_path
, partname
)
702 part
['start'] = get_file_contents(part_sys_block_path
+ "/start", 0)
703 part
['sectors'] = get_file_contents(part_sys_block_path
+ "/size", 0)
705 part
['sectorsize'] = get_file_contents(
706 part_sys_block_path
+ "/queue/logical_block_size")
707 if not part
['sectorsize']:
708 part
['sectorsize'] = get_file_contents(
709 part_sys_block_path
+ "/queue/hw_sector_size", 512)
710 part
['size'] = float(part
['sectors']) * 512
711 part
['human_readable_size'] = human_readable_size(float(part
['sectors']) * 512)
713 for holder
in os
.listdir(part_sys_block_path
+ '/holders'):
714 part
['holders'].append(holder
)
716 partition_metadata
[partname
] = part
717 return partition_metadata
720 def is_mapper_device(device_name
):
721 return device_name
.startswith(('/dev/mapper', '/dev/dm-'))
724 def is_locked_raw_device(disk_path
):
726 A device can be locked by a third party software like a database.
727 To detect that case, the device is opened in Read/Write and exclusive mode
729 open_flags
= (os
.O_RDWR | os
.O_EXCL
)
734 fd
= os
.open(disk_path
, open_flags
, open_mode
)
746 class AllowLoopDevices(object):
752 val
= os
.environ
.get("CEPH_VOLUME_ALLOW_LOOP_DEVICES", "false").lower()
753 if val
not in ("false", 'no', '0'):
757 "CEPH_VOLUME_ALLOW_LOOP_DEVICES is set in your "
758 "environment, so we will allow the use of unattached loop"
759 " devices as disks. This feature is intended for "
760 "development purposes only and will never be supported in"
761 " production. Issues filed based on this behavior will "
768 allow_loop_devices
= AllowLoopDevices()
771 def get_block_devs_sysfs(_sys_block_path
='/sys/block', _sys_dev_block_path
='/sys/dev/block'):
772 def holder_inner_loop():
773 for holder
in holders
:
774 # /sys/block/sdy/holders/dm-8/dm/uuid
775 holder_dm_type
= get_file_contents(os
.path
.join(_sys_block_path
, dev
, f
'holders/{holder}/dm/uuid')).split('-')[0].lower()
776 if holder_dm_type
== 'mpath':
779 # First, get devices that are _not_ partitions
781 dev_names
= os
.listdir(_sys_block_path
)
782 for dev
in dev_names
:
783 name
= kname
= os
.path
.join("/dev", dev
)
784 if not os
.path
.exists(name
):
787 holders
= os
.listdir(os
.path
.join(_sys_block_path
, dev
, 'holders'))
788 if get_file_contents(os
.path
.join(_sys_block_path
, dev
, 'removable')) == "1":
790 if holder_inner_loop():
792 dm_dir_path
= os
.path
.join(_sys_block_path
, dev
, 'dm')
793 if os
.path
.isdir(dm_dir_path
):
794 dm_type
= get_file_contents(os
.path
.join(dm_dir_path
, 'uuid'))
795 type_
= dm_type
.split('-')[0].lower()
796 basename
= get_file_contents(os
.path
.join(dm_dir_path
, 'name'))
797 name
= os
.path
.join("/dev/mapper", basename
)
798 if dev
.startswith('loop'):
799 if not allow_loop_devices():
801 # Skip loop devices that are not attached
802 if not os
.path
.exists(os
.path
.join(_sys_block_path
, dev
, 'loop')):
805 result
.append([kname
, name
, type_
])
806 # Next, look for devices that _are_ partitions
807 for item
in os
.listdir(_sys_dev_block_path
):
808 is_part
= get_file_contents(os
.path
.join(_sys_dev_block_path
, item
, 'partition')) == "1"
809 dev
= os
.path
.basename(os
.readlink(os
.path
.join(_sys_dev_block_path
, item
)))
812 name
= kname
= os
.path
.join("/dev", dev
)
813 result
.append([name
, kname
, "part"])
814 return sorted(result
, key
=lambda x
: x
[0])
817 def get_devices(_sys_block_path
='/sys/block', device
=''):
819 Captures all available block devices as reported by lsblk.
820 Additional interesting metadata like sectors, size, vendor,
821 solid/rotational, etc. is collected from /sys/block/<device>
823 Returns a dictionary, where keys are the full paths to devices.
825 ..note:: loop devices, removable media, and logical volumes are never included.
830 block_devs
= get_block_devs_sysfs(_sys_block_path
)
832 block_types
= ['disk', 'mpath']
833 if allow_loop_devices():
834 block_types
.append('loop')
836 for block
in block_devs
:
837 devname
= os
.path
.basename(block
[0])
839 if block
[2] not in block_types
:
841 sysdir
= os
.path
.join(_sys_block_path
, devname
)
844 # If the device is ceph rbd it gets excluded
845 if is_ceph_rbd(diskname
):
848 # If the mapper device is a logical volume it gets excluded
849 if is_mapper_device(diskname
):
850 if lvm
.get_device_lvs(diskname
):
853 # all facts that have no defaults
854 # (<name>, <path relative to _sys_block_path>)
855 facts
= [('removable', 'removable'),
857 ('vendor', 'device/vendor'),
858 ('model', 'device/model'),
859 ('rev', 'device/rev'),
860 ('sas_address', 'device/sas_address'),
861 ('sas_device_handle', 'device/sas_device_handle'),
862 ('support_discard', 'queue/discard_granularity'),
863 ('rotational', 'queue/rotational'),
864 ('nr_requests', 'queue/nr_requests'),
866 for key
, file_
in facts
:
867 metadata
[key
] = get_file_contents(os
.path
.join(sysdir
, file_
))
869 device_slaves
= os
.listdir(os
.path
.join(sysdir
, 'slaves'))
871 metadata
['device_nodes'] = ','.join(device_slaves
)
873 metadata
['device_nodes'] = devname
875 metadata
['scheduler_mode'] = ""
876 scheduler
= get_file_contents(sysdir
+ "/queue/scheduler")
877 if scheduler
is not None:
878 m
= re
.match(r
".*?(\[(.*)\])", scheduler
)
880 metadata
['scheduler_mode'] = m
.group(2)
882 metadata
['partitions'] = get_partitions_facts(sysdir
)
884 size
= get_file_contents(os
.path
.join(sysdir
, 'size'), 0)
886 metadata
['sectors'] = get_file_contents(os
.path
.join(sysdir
, 'sectors'), 0)
887 fallback_sectorsize
= get_file_contents(sysdir
+ "/queue/hw_sector_size", 512)
888 metadata
['sectorsize'] = get_file_contents(sysdir
+
889 "/queue/logical_block_size",
891 metadata
['size'] = float(size
) * 512
892 metadata
['human_readable_size'] = human_readable_size(metadata
['size'])
893 metadata
['path'] = diskname
894 metadata
['locked'] = is_locked_raw_device(metadata
['path'])
895 metadata
['type'] = block
[2]
897 device_facts
[diskname
] = metadata
900 def has_bluestore_label(device_path
):
902 bluestoreDiskSignature
= 'bluestore block device' # 22 bytes long
904 # throws OSError on failure
905 logger
.info("opening device {} to check for BlueStore label".format(device_path
))
907 with
open(device_path
, "rb") as fd
:
908 # read first 22 bytes looking for bluestore disk signature
909 signature
= fd
.read(22)
910 if signature
.decode('ascii', 'replace') == bluestoreDiskSignature
:
912 except IsADirectoryError
:
913 logger
.info(f
'{device_path} is a directory, skipping.')