]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume util.disk create a device metadata utility
authorAlfredo Deza <adeza@redhat.com>
Thu, 3 May 2018 14:51:47 +0000 (10:51 -0400)
committerAlfredo Deza <adeza@redhat.com>
Wed, 1 Aug 2018 15:38:28 +0000 (11:38 -0400)
Signed-off-by: Alfredo Deza <adeza@redhat.com>
(cherry picked from commit c6559538db28d6b8bc8529e6b2a62e8619c69bd6)

src/ceph-volume/ceph_volume/util/disk.py

index 5c69920efbe44c12a4e8323d7d41004cb89d641d..21b12d475b313c9c44b77c7343fd6bf3917bd89f 100644 (file)
@@ -1,10 +1,10 @@
-import errno
 import logging
 import os
 import re
 import stat
-import sys
 from ceph_volume import process
+from ceph_volume.api import lvm
+from ceph_volume.util.system import get_file_contents
 
 
 logger = logging.getLogger(__name__)
@@ -266,11 +266,11 @@ def _map_dev_paths(_path, include_abspath=False, include_realpath=False):
         mapping[dev_name] = os.path.join(_path, dev_name)
 
     if include_abspath:
-        for k, v in mapping.items():
+        for k, v in list(mapping.items()):
             mapping[v] = k
 
     if include_realpath:
-        for abspath in mapping.values():
+        for abspath in list(mapping.values()):
             if not os.path.islink(abspath):
                 continue
 
@@ -293,7 +293,7 @@ def get_block_devs(sys_block_path="/sys/block", skip_loop=True):
     devices = _map_dev_paths(sys_block_path).keys()
     if skip_loop:
         return [d for d in devices if not d.startswith('loop')]
-    return devices
+    return list(devices)
 
 
 def get_dev_devs(dev_path="/dev"):
@@ -314,3 +314,119 @@ def get_mapper_devs(mapper_path="/dev/mapper"):
     required for proper operation.
     """
     return _map_dev_paths(mapper_path, include_abspath=True, include_realpath=True)
+
+
+def human_readable_size(size):
+    """
+    Take a size in bytes, and transform it into a human readable size with up
+    to two decimals of precision.
+    """
+    suffixes = ['B', 'KB', 'MB', 'GB', 'TB']
+    suffix_index = 0
+    while size > 1024:
+        suffix_index += 1
+        size = size / 1024.0
+    return "{size:.2f} {suffix}".format(
+        size=size,
+        suffix=suffixes[suffix_index])
+
+
+def get_partitions_facts(sys_block_path):
+    partition_metadata = {}
+    for folder in os.listdir(sys_block_path):
+        folder_path = os.path.join(sys_block_path, folder)
+        if os.path.exists(os.path.join(folder_path, 'partition')):
+            contents = get_file_contents(os.path.join(folder_path, 'partition'))
+            if '1' in contents:
+                part = {}
+                partname = folder
+                part_sys_block_path = os.path.join(sys_block_path, partname)
+
+                part['start'] = get_file_contents(part_sys_block_path + "/start", 0)
+                part['sectors'] = get_file_contents(part_sys_block_path + "/size", 0)
+
+                part['sectorsize'] = get_file_contents(
+                    part_sys_block_path + "/queue/logical_block_size")
+                if not part['sectorsize']:
+                    part['sectorsize'] = get_file_contents(
+                        part_sys_block_path + "/queue/hw_sector_size", 512)
+                part['size'] = human_readable_size(float(part['sectors']) * 512)
+
+                partition_metadata[partname] = part
+    return partition_metadata
+
+
+def get_devices(_sys_block_path='/sys/block', _dev_path='/dev', _mapper_path='/dev/mapper'):
+    """
+    Captures all available devices from /sys/block/, including its partitions,
+    along with interesting metadata like sectors, size, vendor,
+    solid/rotational, etc...
+
+    Returns a dictionary, where keys are the full paths to devices.
+
+    ..note:: dmapper devices get their path updated to what they link from, if
+            /dev/dm-0 is linked by /dev/mapper/ceph-data, then the latter gets
+            used as the key.
+
+    ..note:: loop devices, removable media, and logical volumes are never included.
+    """
+    # Portions of this detection process are inspired by some of the fact
+    # gathering done by Ansible in module_utils/facts/hardware/linux.py. The
+    # processing of metadata and final outcome *is very different* and fully
+    # imcompatible. There are ignored devices, and paths get resolved depending
+    # on dm devices, loop, and removable media
+
+    device_facts = {}
+
+    block_devs = get_block_devs(_sys_block_path)
+    dev_devs = get_dev_devs(_dev_path)
+    mapper_devs = get_mapper_devs(_mapper_path)
+
+    for block in block_devs:
+        sysdir = os.path.join(_sys_block_path, block)
+        metadata = {}
+
+        # Ensure that the diskname is an absolute path and that it never points
+        # to a /dev/dm-* device
+        diskname = mapper_devs.get(block) or dev_devs.get(block)
+
+        # If the mapper device is a logical volume it gets excluded
+        if diskname.startswith(('/dev/mapper', '/dev/dm-')):
+            if lvm.is_lv(diskname):
+                continue
+
+        # If the device reports itself as 'removable', get it excluded
+        metadata['removable'] = get_file_contents(os.path.join(sysdir, 'removable'))
+        if metadata['removable'] == '1':
+            continue
+
+        for key in ['vendor', 'model', 'sas_address', 'sas_device_handle']:
+            metadata[key] = get_file_contents(sysdir + "/device/" + key)
+
+        for key in ['sectors', 'size']:
+            metadata[key] = get_file_contents(os.path.join(sysdir, key), 0)
+
+        for key, _file in [('support_discard', '/queue/discard_granularity')]:
+            metadata[key] = get_file_contents(os.path.join(sysdir, _file))
+
+        metadata['partitions'] = get_partitions_facts(sysdir)
+
+        metadata['rotational'] = get_file_contents(sysdir + "/queue/rotational")
+        metadata['scheduler_mode'] = ""
+        scheduler = get_file_contents(sysdir + "/queue/scheduler")
+        if scheduler is not None:
+            m = re.match(r".*?(\[(.*)\])", scheduler)
+            if m:
+                metadata['scheduler_mode'] = m.group(2)
+
+        if not metadata['sectors']:
+            metadata['sectors'] = 0
+        size = metadata['sectors'] or metadata['size']
+        metadata['sectorsize'] = get_file_contents(sysdir + "/queue/logical_block_size")
+        if not metadata['sectorsize']:
+            metadata['sectorsize'] = get_file_contents(sysdir + "/queue/hw_sector_size", 512)
+        metadata['human_readable_size'] = human_readable_size(float(size) * 512)
+        metadata['size'] = human_readable_size(float(size) * 512)
+
+        device_facts[diskname] = metadata
+    return device_facts