* storage/__init__.py (createSuggestedVGName): Take into account the new lvm black list. * storage/devicelibs/lvm.py (blacklistVG): New function, add a VG to a the black list. * storage/devicelibs/lvm.py (vgreduce): introduces a new argument to the function. rm means use the lvm --removemissing option. * storage/devices.py (LVMVolumeGroupDevice.complete): New function to evaluate if VG is consistent. * storage/devices.py (LVMLogicalVolumeDevice.complete): Likewise for LVs. * storage/devicetree.py (_handleInconsistencies): New function intended to catch all unwanted behavior from the system before continuing. * storage/devicetree.py (questionReinitILVM): New function intended to ask the user what to do in case we find inconsistent LVM metadata. --- iw/partition_gui.py | 2 +- storage/__init__.py | 6 ++- storage/devicelibs/lvm.py | 27 +++++++--- storage/devices.py | 24 +++++++- storage/devicetree.py | 124 ++++++++++++++++++++++++++++++++++++++++++- storage/formats/__init__.py | 3 + storage/formats/lvmpv.py | 6 ++- 7 files changed, 176 insertions(+), 16 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 0b52a70..6ce1b11 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -819,7 +819,7 @@ class PartitionWindow(InstallWindow): partName = part.getDeviceNodeName().split("/")[-1] device = self.storage.devicetree.getDeviceByName(partName) if not device and not part.type & parted.PARTITION_FREESPACE: - raise RuntimeError("can't find partition %s in device" + log.debug("can't find partition %s in device" " tree" % partName) # ignore the tiny < 1 MB partitions (#119479) diff --git a/storage/__init__.py b/storage/__init__.py index 7b7ab5c..6f181d3 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -632,13 +632,15 @@ class Storage(object): else: vgtemplate = "VolGroup" - if vgtemplate not in vgnames: + if vgtemplate not in vgnames and \ + vgtemplate not in lvm.lvm_vg_blacklist: return vgtemplate else: i = 0 while 1: tmpname = "%s%02d" % (vgtemplate, i,) - if not tmpname in vgnames: + if not tmpname in vgnames and \ + tmpname not in lvm.lvm_vg_blacklist: break i += 1 diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 8b755a4..aa93071 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -73,7 +73,7 @@ def _composeConfig(): if len(rejects) > 0: for i in range(len(rejects)): - filter_string = filter_string + ("\"r|%s|\", " % rejects[i]) + filter_string = filter_string + ("\"r|%s|\"," % rejects[i]) filter_string = " filter=[%s] " % filter_string.strip(",") @@ -86,7 +86,7 @@ def _composeConfig(): # devices_string can have (inside the brackets) "dir", "scan", # "preferred_names", "filter", "cache_dir", "write_cache_state", # "types", "sysfs_scan", "md_component_detection". see man lvm.conf. - devices_string = " devices { %s } " % (filter_string) # strings can be added + devices_string = " devices {%s} " % (filter_string) # strings can be added config_string = devices_string # more strings can be added. config_args = ["--config", config_string] @@ -99,6 +99,12 @@ def lvm_cc_addFilterRejectRegexp(regexp): _composeConfig() # End config_args handling code. +# Names that should not be used int the creation of VGs +lvm_vg_blacklist = [] +def blacklistVG(name): + global lvm_vg_blacklist + lvm_vg_blacklist.append(name) + def getPossiblePhysicalExtents(floor=0): """Returns a list of integers representing the possible values for the physical extent of a volume group. Value is in KB. @@ -283,11 +289,18 @@ def vgdeactivate(vg_name): if rc: raise LVMError("vgdeactivate failed for %s" % vg_name) -def vgreduce(vg_name, pv_list): - args = ["vgreduce"] + \ - config_args + \ - [vg_name] + \ - pv_list +def vgreduce(vg_name, pv_list, rm=False): + """ Reduce a VG. + + rm -> with RemoveMissing option. + Use pv_list when rm=False, otherwise ignore pv_list and call vgreduce with + the --removemissing option. + """ + args = ["vgreduce"] + if rm: + args.extend(["--removemissing", vg_name]) + else: + args.extend([vg_name] + pv_list) rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", diff --git a/storage/devices.py b/storage/devices.py index 1a52c71..62bc804 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1633,9 +1633,15 @@ class LVMVolumeGroupDevice(DMDevice): self.teardown() - lvm.vgremove(self.name) - self.notifyKernel() - self.exists = False + # this sometimes fails for some reason. + try: + lvm.vgreduce(self.name, [], rm=True) + lvm.vgremove(self.name) + except lvm.LVMErorr: + raise DeviceError("Could not completely remove VG %s" % self.name) + finally: + self.notifyKernel() + self.exists = False def reduce(self, pv_list): """ Remove the listed PVs from the VG. """ @@ -1764,6 +1770,13 @@ class LVMVolumeGroupDevice(DMDevice): """ A list of this VG's LVs """ return self._lvs[:] # we don't want folks changing our list + @property + def complete(self): + """Check if the vg has all its pvs in the system + Return True if complete. + """ + return len(self.pvs) == self.pvCount or not self.exists + class LVMLogicalVolumeDevice(DMDevice): """ An LVM Logical Volume """ @@ -1863,6 +1876,11 @@ class LVMLogicalVolumeDevice(DMDevice): """ The LV's name (not including VG name). """ return self._name + @property + def complete(self): + """ Test if vg exits and if it has all pvs. """ + return self.vg.complete + def setup(self, intf=None): """ Open, or set up, a device. """ log_method_call(self, self.name, status=self.status) diff --git a/storage/devicetree.py b/storage/devicetree.py index 2b997b5..3431cac 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -136,6 +136,36 @@ def questionInitializeDisk(intf=None, name=None): retVal = True return retVal +def questionReinitILVM(intf=None, pv_names=None, lv_name=None, vg_name=None): + retVal = False # The less destructive default + if not intf or not pv_names or (lv_name is None and vg_name is None): + pass + else: + if vg_name is not None: + message = "%s Volume Group" % vg_name + elif lv_name is not None: + message = "%s Logical Volume" % lv_name + + + rc = intf.messageWindow(_("Warning"), + _("Error processing LVM.\n" + "It seems that there is inconsistent LVM data. " + "(%s) make(s) up %s. " + "You can reinitialize all related PVs, which will " + "erase all LVM metadata. Or ignore, which will " + "preserve contents.") + %(str(pv_names), message), + type="custom", + custom_buttons = [ _("_Ignore drive(s)"), + _("_Re-initialize drive(s)") ], + custom_icon="question") + if rc == 0: + pass + else: + retVal = True # thie means clobber. + + return retVal + class DeviceTree(object): """ A quasi-tree that represents the devices in the system. @@ -652,7 +682,7 @@ class DeviceTree(object): log.debug("added %s (%s) to device tree" % (newdev.name, newdev.type)) - def _removeDevice(self, dev, force=None): + def _removeDevice(self, dev, force=None, moddisk=True): """ Remove a device from the tree. Only leaves may be removed. @@ -665,7 +695,8 @@ class DeviceTree(object): raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) # if this is a partition we need to remove it from the parted.Disk - if isinstance(dev, PartitionDevice) and dev.disk is not None: + if moddisk and isinstance(dev, PartitionDevice) and \ + dev.disk is not None: # if this partition hasn't been allocated it could not have # a disk attribute dev.disk.partedDisk.removePartition(dev.partedPartition) @@ -1328,12 +1359,96 @@ class DeviceTree(object): size=lv_size, exists=True) self._addDevice(lv_device) + try: lv_device.setup() except DeviceError as e: log.info("setup of %s failed: %s" % (lv_device.name, e)) + def _handleInconsistencies(self, device): + def reinitializeVG(vg): + # First we remove VG data + try: + vg.destroy() + except DeviceError: + # the pvremoves will finish the job. + log.debug("There was an error destroying the VG %s." % vg.name) + pass + + # remove VG device from list. + self._removeDevice(vg) + + for parent in vg.parents: + parent.format.destroy() + + # Give the vg the a default format + kwargs = {"uuid": parent.uuid, + "label": parent.diskLabel, + "device": parent.path, + "exists": parent.exists} + parent.format = formats.getFormat(*[""], **kwargs) + + if device.type == "lvmvg": + paths = [] + for parent in device.parents: + paths.append(parent.path) + + # when zeroMbr is true he wont ask. + if not device.complete and (self.zeroMbr or \ + questionReinitILVM(intf=self.intf, \ + vg_name=device.name, pv_names=paths)): + reinitializeVG(device) + + elif not device.complete: + # The user chose not to reinitialize. + # hopefully this will ignore the vg components too. + self._removeDevice(vg) + lvm.lvm_cc_addFilterRejectRegexp(vg.name) + lvm.blacklistVG(vg.name) + for parent in vg.parents: + self._removeDevice(parent, moddisk=False) + lvm.lvm_cc_addFilterRejectRegexp(parent.name) + + return + + elif device.type == "lvmlv": + # we might have already fixed this. + if device not in self._devices or \ + device.name in self._ignoredDisks: + return + + paths = [] + for parent in device.vg.parents: + paths.append(parent.path) + + if not device.complete and (self.zeroMbr or \ + questionReinitILVM(intf=self.intf, \ + lv_name=device.name, pv_names=paths)): + + # destroy all lvs. + for lv in device.vg.lvs: + lv.destroy() + device.vg._removeLogVol(lv) + self._removeDevice(lv) + + reinitializeVG(device.vg) + + elif not device.complete: + # ignore all the lvs. + for lv in device.vg.lvs: + self._removeDevice(lv) + lvm.lvm_cc_addFilterRejectRegexp(lv.name) + # ignore the vg + self._removeDevice(device.vg) + lvm.lvm_cc_addFilterRejectRegexp(device.vg.name) + lvm.blacklistVG(device.vg.name) + # ignore all the pvs + for parent in device.vg.parents: + self._removeDevice(parent, moddisk=False) + lvm.lvm_cc_addFilterRejectRegexp(parent.name) + return + def populate(self): """ Locate all storage devices. """ # each iteration scans any devices that have appeared since the @@ -1363,6 +1478,11 @@ class DeviceTree(object): for dev in devices: self.addUdevDevice(dev) + # After having the complete tree we make sure that the system + # inconsistencies are ignored or resolved. + for leaf in self.leaves: + self._handleInconsistencies(leaf) + self.teardownAll() def teardownAll(self): diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 3eaeb0f..63d6dac 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -253,6 +253,9 @@ class DeviceFormat(object): os.lseek(fd, -1024 * 1024, 2) os.write(fd, buf) os.close(fd) + except OSError as e: + if e.errno == 28: # No space left in device + pass except Exception, e: log.error("error zeroing out %s: %s" % (self.device, e)) diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index cc243eb..c9cbe29 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -106,7 +106,11 @@ class LVMPhysicalVolume(DeviceFormat): raise PhysicalVolumeError("device is active") # FIXME: verify path exists? - lvm.pvremove(self.device) + try: + lvm.pvremove(self.device) + except LVMError: + DeviceFormat.destroy(self, *args, **kwargs) + self.exists = False self.notifyKernel() -- 1.6.0.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list