From ba2badbc40aa1798ee667b6b4b31f5733b50611a Mon Sep 17 00:00:00 2001 From: mulhern Date: Thu, 10 Dec 2015 17:46:10 -0500 Subject: Make use of pyudev package as much as possible in utils module. Signed-off-by: mulhern --- rtslib/utils.py | 117 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/rtslib/utils.py b/rtslib/utils.py index 4cee7a5..e5f1c4d 100644 --- a/rtslib/utils.py +++ b/rtslib/utils.py @@ -27,6 +27,10 @@ import subprocess import uuid from contextlib import contextmanager +import pyudev + +_CONTEXT = pyudev.Context() + class RTSLibError(Exception): ''' Generic rtslib error. @@ -111,22 +115,36 @@ def is_dev_in_use(path): os.close(file_fd) return False +def _get_size_for_dev(device): + ''' + @param device: the device + @type device: pyudev.Device + @return: the size in logical blocks, 0 if none found + @rtype: int + ''' + attributes = device.attributes + try: + sect_size = attributes.asint('size') + except (KeyError, UnicodeDecodeError, ValueError): + return 0 + + try: + logical_block_size = attributes.asint('queue/logical_block_size') + except (KeyError, UnicodeDecodeError, ValueError): + return 0 + + return (sect_size * 512) // logical_block_size + def get_size_for_blk_dev(path): ''' @param path: The path to a block device @type path: string @return: The size in logical blocks of the device + @raises: DeviceNotFoundError if corresponding device not found + @raises: EnvironmentError, ValueError in some situations ''' - path = os.path.realpath(str(path)) - rdev = os.lstat(path).st_rdev - maj, min = os.major(rdev), os.minor(rdev) - - for line in list(open("/proc/partitions"))[2:]: - xmaj, xmin, size, name = line.split() - if (maj, min) == (int(xmaj), int(xmin)): - return get_size_for_disk_name(name) - else: - return 0 + device = Device.from_device_file(_CONTEXT, os.path.realpath(str(path))) + return _get_size_for_dev(device) get_block_size = get_size_for_blk_dev @@ -135,23 +153,26 @@ def get_size_for_disk_name(name): @param name: a kernel disk name, as found in /proc/partitions @type name: string @return: The size in logical blocks of a disk-type block device. + @raises: DeviceNotFoundError ''' # size is in 512-byte sectors, we want to return number of logical blocks - def get_size(path, is_partition=False): - sect_size = int(fread("%s/size" % path)) - if is_partition: - path = os.path.split(path)[0] - logical_block_size = int(fread("%s/queue/logical_block_size" % path)) - return sect_size / (logical_block_size / 512) + def get_size(name): + """ + :param str name: name of block device + :raises DeviceNotFoundError: if device not found + """ + device = pyudev.Device.from_name(_CONTEXT, 'block', name) + return _get_size_for_dev(device) # Disk names can include '/' (e.g. 'cciss/c0d0') but these are changed to # '!' when listed in /sys/block. + # in pyudev 0.19 it should no longer be necessary to swap '/'s in name name = name.replace("/", "!") try: - return get_size("/sys/block/%s" % name) - except IOError: + return get_size(name) + except pyudev.DeviceNotFoundError: # Maybe it's a partition? m = re.search(r'^([a-z0-9_\-!]+?)(\d+)$', name) if m: @@ -160,7 +181,7 @@ def get_size_for_disk_name(name): disk = m.groups()[0] if disk[-1] == 'p' and disk[-2].isdigit(): disk = disk[:-1] - return get_size("/sys/block/%s/%s" % (disk, m.group()), True) + return get_size(m.group()) else: raise @@ -184,26 +205,41 @@ def get_blockdev_type(path): @type path: string @return: An int for the block device type, or None if not a block device. ''' - dev = os.path.realpath(path) - - # is dev a block device? try: - mode = os.stat(dev) - except OSError: + device = pyudev.Device.from_device_file(_CONTEXT, path) + except (pyudev.DeviceNotFoundError, EnvironmentError, ValueError): return None - if not stat.S_ISBLK(mode[stat.ST_MODE]): + if device.subsystem != u'block': return None - # assume disk if device/type is missing - disk_type = 0 - with ignored(IOError): - disk_type = int(fread("/sys/block/%s/device/type" % os.path.basename(dev))) + attributes = device.attributes + disk_type = 0 + try: + disk_type = attributes.asint('device/type') + except (KeyError, UnicodeDecodeError, ValueError): + pass return disk_type get_block_type = get_blockdev_type +def _hctl_from_dev(device): + ''' + @param device: the device + @type device: pyudev.Device + @returns: H:C:T:L specifier or None if not found + @rtype: list of int * 4 or NoneType + ''' + parent = device.parent + if parent is None or parent.subsystem != 'scsi': + return None + + try: + return [int(data) for data in parent.sys_name.split(':')] + except (ValueError, TypeError): + return None + def convert_scsi_path_to_hctl(path): ''' This function returns the SCSI ID in H:C:T:L form for the block @@ -229,14 +265,11 @@ def convert_scsi_path_to_hctl(path): values representing the SCSI ID of the device, or None if no match is found. ''' - devname = os.path.basename(os.path.realpath(path)) try: - hctl = os.listdir("/sys/block/%s/device/scsi_device" - % devname)[0].split(':') - except: + device = pyudev.Device.from_device_file(context, os.path.realpath(path)) + except (pyudev.DeviceNotFoundError, EnvironmentError, ValueError): return None - - return [int(data) for data in hctl] + return _hctl_from_dev(device) def convert_scsi_hctl_to_path(host, controller, target, lun): ''' @@ -270,11 +303,15 @@ def convert_scsi_hctl_to_path(host, controller, target, lun): raise RTSLibError( "The host, controller, target and lun parameter must be integers") - for devname in os.listdir("/sys/block"): - path = "/dev/%s" % devname - hctl = [host, controller, target, lun] - if convert_scsi_path_to_hctl(path) == hctl: - return os.path.realpath(path) + hctl = [host, controller, target, lun] + try: + scsi_device = pyudev.Device.from_name(_CONTEXT, 'scsi', ':'.join(hctl)) + except DeviceNotFoundError: + return '' + + for device in context.list_devices(subsystem='block'): + if device.parent == scsi_device: + return device.device_node return '' def generate_wwn(wwn_type): -- cgit v1.2.1