def probe(context=None, report=False): """Initiate an mdadm assemble to awaken existing MDADM devices. For each md block device, extract required information needed to describe the array for recreation or reuse as needed. mdadm tooling provides information about the raid type, the members, the size, the name, uuids, metadata version. """ mdadm_assemble() # ignore passed context, must read udev after assembling mdadm devices context = pyudev.Context() raids = {} for device in sane_block_devices(context): if 'MD_NAME' in device and device.get('DEVTYPE') == 'disk': devname = device['DEVNAME'] attrs = udev_get_attributes(device) attrs['size'] = str(read_sys_block_size_bytes(devname)) devices, spares = get_mdadm_array_members(devname, device) cfg = dict(device) cfg.update({'raidlevel': device['MD_LEVEL'], 'devices': devices, 'spare_devices': spares}) raids[devname] = cfg return raids
def probe(context=None, report=False): """ Probing for dm_crypt devices requires running dmsetup info commands to collect how a particular dm-X device is composed. """ # ignore supplied context, we need to read udev after scan/vgchange context = pyudev.Context() crypt_devices = {} # look for block devices with DM_UUID and CRYPT; these are crypt devices for device in sane_block_devices(context): if 'DM_UUID' in device and device['DM_UUID'].startswith('CRYPT'): devname = device['DEVNAME'] dm_info = dmsetup_info(devname) crypt_devices[dm_info['name']] = dm_info return crypt_devices
def blockdev_probe(context=None): """ Non-class method for extracting relevant block devices from pyudev.Context(). """ def _extract_partition_table(devname): cmd = ['sfdisk', '--bytes', '--json', devname] try: result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) output = result.stdout.decode('utf-8') except subprocess.CalledProcessError as e: log.error('Failed to probe partition table on %s:%s', devname, e) return None if not output: return None ptable = {} try: ptable = json.loads(output) except json.decoder.JSONDecodeError: log.exception('Failed to load sfdisk json output:') return ptable if not context: context = pyudev.Context() blockdev = {} for device in sane_block_devices(context): if device['MAJOR'] not in ["1", "7"]: attrs = udev_get_attributes(device) # update the size attr as it may only be the number # of blocks rather than size in bytes. attrs['size'] = \ str(read_sys_block_size_bytes(device['DEVNAME'])) blockdev[device['DEVNAME']] = dict(device) blockdev[device['DEVNAME']].update({'attrs': attrs}) # include partition table info if present ptable = _extract_partition_table(device['DEVNAME']) if ptable: blockdev[device['DEVNAME']].update(ptable) return blockdev
def probe(context=None): """ Capture detected filesystems found on discovered block devices. """ filesystems = {} if not context: context = pyudev.Context() for device in sane_block_devices(context): # Ignore block major=1 (ramdisk) and major=7 (loopback) # these won't ever be used in recreating storage on target systems. if device['MAJOR'] not in ["1", "7"]: fs_info = get_device_filesystem(device) # The ID_FS_ udev values come from libblkid, which contains code to # recognize lots of different things that block devices or their # partitions can contain (filesystems, lvm PVs, bcache, ...). We # only want to report things that are mountable filesystems here, # which libblkid conveniently tags with ID_FS_USAGE=filesystem. # Swap is a bit of a special case because it is not a mountable # filesystem in the usual sense, but subiquity still needs to # generate mount actions for it. if fs_info.get("USAGE") == "filesystem" or \ fs_info.get("TYPE") == "swap": filesystems[device['DEVNAME']] = fs_info return filesystems
def probe(context=None): """ Probing for LVM devices requires initiating a kernel level scan of block devices to look for physical volumes, volume groups and logical volumes. Once detected, the prober will activate any volume groups detected. The prober will refresh the udev context which brings in addition information relating to LVM devices. This prober relies on udev detecting devices via the 'DM_UUID' field and for each of such devices, the prober records the logical volume. For each logical volume, the prober determines the hosting volume_group and records detailed information about the group including members. The process is repeated to determine the underlying physical volumes that are used to construct a volume group. Care is taken to handle scenarios where physical volumes are not yet allocated to a volume group (such as a linear VG). On newer systems (Disco+) the lvm2 software stack provides a rich reporting data dump in JSON format. On systems with older LVM2 stacks, the LVM probe may be incomplete. """ # scan and activate lvm vgs/lvs lvm_scan() activate_volgroups() # ignore supplied context, we need to read udev after scan/vgchange context = pyudev.Context() lvols = {} vgroups = {} pvols = {} vg_report = probe_vgs_report() for device in sane_block_devices(context): if 'DM_UUID' in device and device['DM_UUID'].startswith('LVM'): (lv_id, new_lv) = extract_lvm_partition(device) if lv_id not in lvols: lvols[lv_id] = new_lv else: log.error('Found duplicate logical volume: %s', lv_id) continue vg_name = device['DM_VG_NAME'] (vg_id, new_vg) = extract_lvm_volgroup(vg_name, vg_report) if vg_id not in vgroups: vgroups[vg_id] = new_vg else: log.error('Found duplicate volume group: %s', vg_id) continue if vg_id not in pvols: pvols[vg_id] = new_vg['devices'] lvm = {} if lvols: lvm.update({'logical_volumes': lvols}) if pvols: lvm.update({'physical_volumes': pvols}) if vgroups: lvm.update({'volume_groups': vgroups}) return lvm