Adds support for scanning existing btrfs, including subvolumes. Also adds an optional arg to getDeviceByPath to accommodate the fact that btrfs volumes don't have a separate device node to represent the top-level volume. btrfs format.uuid is the member device's UUID (ID_FS_UUID_SUB), while format.volUUID (ID_FS_UUID) is the top-level volume's UUID. This prevents some further madness in which multiple devices would have the same UUID. --- pyanaconda/storage/devices.py | 15 ++++--- pyanaconda/storage/devicetree.py | 80 ++++++++++++++++++++++++++++++------ pyanaconda/storage/formats/fs.py | 2 +- pyanaconda/storage/partitioning.py | 4 +- 4 files changed, 79 insertions(+), 22 deletions(-) diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py index de78602..0e641f3 100644 --- a/pyanaconda/storage/devices.py +++ b/pyanaconda/storage/devices.py @@ -3941,18 +3941,21 @@ class BTRFSVolumeDevice(BTRFSDevice): self.subvolumes = [] for parent in self.parents: + if parent.format.type != "btrfs": + raise ValueError("member device %s is not BTRFS" % parent.name) + if parent.format.exists and self.exists and \ - parent.format.uuid != self.uuid: + parent.format.volUUID != self.uuid: raise ValueError("BTRFS member device %s UUID %s does not " "match volume UUID %s" % (parent.name, - parent.format.uuid, - self.uuid)) + parent.format.volUUID, self.uuid)) if self.parents and not self.format.type: label = getattr(self.parents[0].format, "label", None) - self.format = getFormat("btrfs", exists=self.exists, + self.format = getFormat("btrfs", + exists=self.exists, label=label, - uuid=self.uuid, + volUUID=self.uuid, device=self.path) label = getattr(self.format, "label", None) @@ -3980,7 +3983,7 @@ class BTRFSVolumeDevice(BTRFSDevice): if device.format.type != "btrfs": raise ValueError("addDevice requires a btrfs device as sole arg") - if device.format.uuid != self.uuid: + if device.format.volUUID != self.uuid: raise ValueError("device UUID does not match the volume UUID") if device in self.parents: diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py index fb40cf6..f4ac54c 100644 --- a/pyanaconda/storage/devicetree.py +++ b/pyanaconda/storage/devicetree.py @@ -336,7 +336,7 @@ class DeviceTree(object): Raise ValueError if the device's identifier is already in the list. """ - if newdev.path in [d.path for d in self._devices] and \ + if newdev.uuid and newdev.uuid in [d.uuid for d in self._devices] and \ not isinstance(newdev, NoDevice): raise ValueError("device is already in tree") @@ -1558,6 +1558,42 @@ class DeviceTree(object): #device.format.raidmem = block.getMemFromRaidSet(dm_array, # major=major, minor=minor, uuid=uuid, name=name) + def handleBTRFSFormat(self, info, device): + log_method_call(self, name=device.name) + name = udev_device_get_name(info) + sysfs_path = udev_device_get_sysfs_path(info) + uuid = udev_device_get_uuid(info) + + btrfs_dev = None + for d in self.devices: + if isinstance(d, BTRFSVolumeDevice) and d.uuid == uuid: + btrfs_dev = d + break + + if btrfs_dev: + log.info("found btrfs volume %s" % btrfs_dev.name) + btrfs_dev._addDevice(device) + else: + label = udev_device_get_label(info) + log.info("creating btrfs volume btrfs.%s" % label) + btrfs_dev = BTRFSVolumeDevice(label, parents=[device], uuid=uuid, + exists=True) + self._addDevice(btrfs_dev) + + if not btrfs_dev.subvolumes: + for subvol_dict in btrfs_dev.listSubVolumes(): + vol_id = subvol_dict["id"] + vol_path = subvol_dict["path"] + if vol_path in [sv.name for sv in btrfs_dev.subvolumes]: + continue + fmt = getFormat("btrfs", device=btrfs_dev.path, exists=True, + mountopts="subvol=%d" % vol_id) + subvol = BTRFSSubVolumeDevice(vol_path, + vol_id=vol_id, + format=fmt, + parents=[btrfs_dev]) + self._addDevice(subvol) + def handleUdevDeviceFormat(self, info, device): log_method_call(self, name=getattr(device, "name", None)) name = udev_device_get_name(info) @@ -1636,6 +1672,11 @@ class DeviceTree(object): apple = formats.getFormat("appleboot") if apple.minSize <= device.size <= apple.maxSize: args[0] = "appleboot" + elif format_type == "btrfs": + # the format's uuid attr will contain the UUID_SUB, while the + # overarching volume UUID will be stored as volUUID + kwargs["uuid"] = info["ID_FS_UUID_SUB"] + kwargs["volUUID"] = uuid try: log.info("type detected on '%s' is '%s'" % (name, format_type,)) @@ -1665,6 +1706,8 @@ class DeviceTree(object): self.handleUdevLVMPVFormat(info, device) elif device.format.type == "multipath_member": self.handleMultipathMemberFormat(info, device) + elif device.format.type == "btrfs": + self.handleBTRFSFormat(info, device) def updateDeviceFormat(self, device): log.info("updating format of device: %s" % device) @@ -2051,21 +2094,30 @@ class DeviceTree(object): log_method_return(self, found) return found - def getDeviceByPath(self, path): + def getDeviceByPath(self, path, preferLeaves=True): log_method_call(self, path=path) if not path: log_method_return(self, None) return None - found = None + leaf = None + other = None for device in self._devices: - if device.path == path: - found = device - break - elif (device.type == "lvmlv" or device.type == "lvmvg") and \ - device.path == path.replace("--","-"): - found = device - break + if (device.path == path or + ((device.type == "lvmlv" or device.type == "lvmvg") and + device.path == path.replace("--","-"))): + if device.isleaf and not leaf: + leaf = device + elif not other: + other = device + + if preferLeaves: + all_devs = [leaf, other] + else: + all_devs = [other, leaf] + all_devs = [d for d in all_devs if d] + if all_devs: + found = all_devs[0] log_method_return(self, found) return found @@ -2082,9 +2134,9 @@ class DeviceTree(object): """ List of device instances """ devices = [] for device in self._devices: - if device.path in [d.path for d in devices] and \ + if device.uuid and device.uuid in [d.uuid for d in devices] and \ not isinstance(device, NoDevice): - raise DeviceTreeError("duplicate paths in device tree") + raise DeviceTreeError("duplicate uuids in device tree") devices.append(device) @@ -2132,7 +2184,9 @@ class DeviceTree(object): """ labels = {} for dev in self._devices: - if dev.format and getattr(dev.format, "label", None): + # don't include btrfs member devices + if getattr(dev.format, "label", None) and \ + (dev.format.type != "btrfs" or isinstance(dev, BTRFSDevice)): labels[dev.format.label] = dev return labels diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py index 3c3229f..0942402 100644 --- a/pyanaconda/storage/formats/fs.py +++ b/pyanaconda/storage/formats/fs.py @@ -1140,8 +1140,8 @@ class BTRFS(FS): # partedSystem = fileSystemType["btrfs"] def __init__(self, *args, **kwargs): - self.uuidSub = kwargs.pop("uuidSub", None) super(BTRFS, self).__init__(*args, **kwargs) + self.volUUID = kwargs.pop("volUUID", None) def create(self, *args, **kwargs): # filesystem creation is done in storage.devicelibs.btrfs.create_volume diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index 4a80a8b..8669c82 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -261,8 +261,8 @@ def _scheduleVolumes(storage, devs): def scheduleShrinkActions(storage): """ Schedule actions to shrink partitions as per autopart selection. """ for (path, size) in storage.shrinkPartitions.items(): - device = storage.devicetree.getDeviceByPath(path) - if not device: + device = storage.devicetree.getDeviceByPath(path, preferLeaves=False) + if not device or not isinstance(device, PartitionDevice): raise StorageError("device %s scheduled for shrink disappeared" % path) storage.devicetree.registerAction(ActionResizeFormat(device, size)) -- 1.7.3.4 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list