From: Alfredo Deza Date: Thu, 3 May 2018 14:51:47 +0000 (-0400) Subject: ceph-volume util.disk create a device metadata utility X-Git-Tag: v14.0.0~189^2~7 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=c6559538db28d6b8bc8529e6b2a62e8619c69bd6;p=ceph.git ceph-volume util.disk create a device metadata utility Signed-off-by: Alfredo Deza --- diff --git a/src/ceph-volume/ceph_volume/util/disk.py b/src/ceph-volume/ceph_volume/util/disk.py index 5c69920efbe4..21b12d475b31 100644 --- a/src/ceph-volume/ceph_volume/util/disk.py +++ b/src/ceph-volume/ceph_volume/util/disk.py @@ -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