def list_block_devices(self): """List all physical block devices The switches we use for lsblk: P for KEY="value" output, b for size output in bytes, d to exclude dependant devices (like md or dm devices), i to ensure ascii characters only, and o to specify the fields we need :return: A list of BlockDevices """ report = utils.execute('lsblk', '-PbdioKNAME,MODEL,SIZE,ROTA,TYPE', check_exit_code=[0])[0] lines = report.split('\n') devices = [] for line in lines: device = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): device[key] = val.strip() # Ignore non disk if device.get('TYPE') != 'disk': continue # Ensure all required keys are at least present, even if blank diff = set(['KNAME', 'MODEL', 'SIZE', 'ROTA']) - set(device.keys()) if diff: raise errors.BlockDeviceError( '%s must be returned by lsblk.' % diff) devices.append(BlockDevice(name='/dev/' + device['KNAME'], model=device['MODEL'], size=int(device['SIZE']), rotational=bool(int(device['ROTA'])))) return devices
def test_error_classes(self): cases = [ (errors.InvalidContentError(DETAILS), SAME_DETAILS), (errors.NotFound(), SAME_CL_DETAILS), (errors.CommandExecutionError(DETAILS), SAME_DETAILS), (errors.InvalidCommandError(DETAILS), SAME_DETAILS), (errors.InvalidCommandParamsError(DETAILS), SAME_DETAILS), (errors.RequestedObjectNotFoundError('type_descr', 'obj_id'), DIFF_CL_DETAILS), (errors.IronicAPIError(DETAILS), SAME_DETAILS), (errors.HeartbeatError(DETAILS), SAME_DETAILS), (errors.LookupNodeError(DETAILS), SAME_DETAILS), (errors.LookupAgentIPError(DETAILS), SAME_DETAILS), (errors.LookupAgentInterfaceError(DETAILS), SAME_DETAILS), (errors.ImageDownloadError('image_id', DETAILS), DIFF_CL_DETAILS), (errors.ImageChecksumError('image_id', '/foo/image_id', 'incorrect', 'correct'), DIFF_CL_DETAILS), (errors.ImageWriteError('device', 'exit_code', 'stdout', 'stderr'), DIFF_CL_DETAILS), (errors.ConfigDriveTooLargeError('filename', 'filesize'), DIFF_CL_DETAILS), (errors.ConfigDriveWriteError('device', 'exit_code', 'stdout', 'stderr'), DIFF_CL_DETAILS), (errors.SystemRebootError('exit_code', 'stdout', 'stderr'), DIFF_CL_DETAILS), (errors.BlockDeviceEraseError(DETAILS), SAME_DETAILS), (errors.BlockDeviceError(DETAILS), SAME_DETAILS), (errors.VirtualMediaBootError(DETAILS), SAME_DETAILS), (errors.UnknownNodeError(), DEFAULT_DETAILS), (errors.UnknownNodeError(DETAILS), SAME_DETAILS), (errors.HardwareManagerNotFound(), DEFAULT_DETAILS), (errors.HardwareManagerNotFound(DETAILS), SAME_DETAILS), (errors.HardwareManagerMethodNotFound('method'), DIFF_CL_DETAILS), (errors.IncompatibleHardwareMethodError(), DEFAULT_DETAILS), (errors.IncompatibleHardwareMethodError(DETAILS), SAME_DETAILS), ] for (obj, check_details) in cases: self._test_class(obj, check_details)
def list_all_block_devices(block_type='disk'): """List all physical block devices The switches we use for lsblk: P for KEY="value" output, b for size output in bytes, d to exclude dependent devices (like md or dm devices), i to ensure ascii characters only, and o to specify the fields/columns we need. Broken out as its own function to facilitate custom hardware managers that don't need to subclass GenericHardwareManager. :param block_type: Type of block device to find :return: A list of BlockDevices """ _udev_settle() # map device names to /dev/disk/by-path symbolic links that points to it by_path_mapping = {} disk_by_path_dir = '/dev/disk/by-path' try: paths = os.listdir(disk_by_path_dir) for path in paths: path = os.path.join(disk_by_path_dir, path) # Turn possibly relative symbolic link into absolute devname = os.path.join(disk_by_path_dir, os.readlink(path)) devname = os.path.abspath(devname) by_path_mapping[devname] = path except OSError as e: LOG.warning( "Path %(path)s is inaccessible, /dev/disk/by-path/* " "version of block device name is unavailable " "Cause: %(error)s", { 'path': disk_by_path_dir, 'error': e }) columns = ['KNAME', 'MODEL', 'SIZE', 'ROTA', 'TYPE'] report = utils.execute('lsblk', '-Pbdi', '-o{}'.format(','.join(columns)), check_exit_code=[0])[0] lines = report.split('\n') context = pyudev.Context() devices = [] for line in lines: device = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): device[key] = val.strip() # Ignore block types not specified if device.get('TYPE') != block_type: LOG.debug( "TYPE did not match. Wanted: {!r} but found: {!r}".format( block_type, line)) continue # Ensure all required columns are at least present, even if blank missing = set(columns) - set(device) if missing: raise errors.BlockDeviceError('%s must be returned by lsblk.' % ', '.join(sorted(missing))) name = os.path.join('/dev', device['KNAME']) try: udev = pyudev.Device.from_device_file(context, name) # pyudev started raising another error in 0.18 except (ValueError, EnvironmentError, pyudev.DeviceNotFoundError) as e: LOG.warning( "Device %(dev)s is inaccessible, skipping... " "Error: %(error)s", { 'dev': name, 'error': e }) extra = {} else: # TODO(lucasagomes): Since lsblk only supports # returning the short serial we are using # ID_SERIAL_SHORT here to keep compatibility with the # bash deploy ramdisk extra = { key: udev.get('ID_%s' % udev_key) for key, udev_key in [('wwn', 'WWN'), ( 'serial', 'SERIAL_SHORT' ), ('wwn_with_extension', 'WWN_WITH_EXTENSION' ), ('wwn_vendor_extension', 'WWN_VENDOR_EXTENSION')] } # NOTE(lucasagomes): Newer versions of the lsblk tool supports # HCTL as a parameter but let's get it from sysfs to avoid breaking # old distros. try: extra['hctl'] = os.listdir('/sys/block/%s/device/scsi_device' % device['KNAME'])[0] except (OSError, IndexError): LOG.warning( 'Could not find the SCSI address (HCTL) for ' 'device %s. Skipping', name) # Not all /dev entries are pointed to from /dev/disk/by-path by_path_name = by_path_mapping.get(name) devices.append( BlockDevice(name=name, model=device['MODEL'], size=int(device['SIZE']), rotational=bool(int(device['ROTA'])), vendor=_get_device_info(device['KNAME'], 'block', 'vendor'), by_path=by_path_name, **extra)) return devices
def list_all_block_devices(block_type='disk'): """List all physical block devices The switches we use for lsblk: P for KEY="value" output, b for size output in bytes, d to exclude dependent devices (like md or dm devices), i to ensure ascii characters only, and o to specify the fields/columns we need. Broken out as its own function to facilitate custom hardware managers that don't need to subclass GenericHardwareManager. :param block_type: Type of block device to find :return: A list of BlockDevices """ _udev_settle() columns = ['KNAME', 'MODEL', 'SIZE', 'ROTA', 'TYPE'] report = utils.execute('lsblk', '-Pbdi', '-o{}'.format(','.join(columns)), check_exit_code=[0])[0] lines = report.split('\n') context = pyudev.Context() devices = [] for line in lines: device = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): device[key] = val.strip() # Ignore block types not specified if device.get('TYPE') != block_type: LOG.debug( "TYPE did not match. Wanted: {!r} but found: {!r}".format( block_type, line)) continue # Ensure all required columns are at least present, even if blank missing = set(columns) - set(device) if missing: raise errors.BlockDeviceError('%s must be returned by lsblk.' % ', '.join(sorted(missing))) name = '/dev/' + device['KNAME'] try: udev = pyudev.Device.from_device_file(context, name) # pyudev started raising another error in 0.18 except (ValueError, EnvironmentError, pyudev.DeviceNotFoundError) as e: LOG.warning( "Device %(dev)s is inaccessible, skipping... " "Error: %(error)s", { 'dev': name, 'error': e }) extra = {} else: # TODO(lucasagomes): Since lsblk only supports # returning the short serial we are using # ID_SERIAL_SHORT here to keep compatibility with the # bash deploy ramdisk extra = { key: udev.get('ID_%s' % udev_key) for key, udev_key in [('wwn', 'WWN'), ( 'serial', 'SERIAL_SHORT' ), ('wwn_with_extension', 'WWN_WITH_EXTENSION' ), ('wwn_vendor_extension', 'WWN_VENDOR_EXTENSION')] } devices.append( BlockDevice(name=name, model=device['MODEL'], size=int(device['SIZE']), rotational=bool(int(device['ROTA'])), vendor=_get_device_vendor(device['KNAME']), **extra)) return devices