* storage/devicelibs/lvm.py (zeroLvmMetadata): zero out the LVM metadata only. This basically means from 513 byte and on. This will also ignore the firs 512 bytes if we are in a partition, but that does not matter because LVM does not touch those bits anyways. * 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. * storage/partitioning.py (clearPartitions): We cannot touch the partitions that are ignored in autopart. --- storage/devicelibs/lvm.py | 37 +++++++++++++-- storage/devices.py | 32 ++++++++++++- storage/devicetree.py | 111 +++++++++++++++++++++++++++++++++++++++++++++ storage/formats/lvmpv.py | 6 ++- storage/partitioning.py | 4 ++ 5 files changed, 181 insertions(+), 9 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 8b755a4..40a6902 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -99,6 +99,26 @@ def lvm_cc_addFilterRejectRegexp(regexp): _composeConfig() # End config_args handling code. +def zeroLvmMetadata(device): + """ This dds from byte 513 to 200Mb+513 of the disk. + + We assume that lvm has nothing to do with the first 512 bytes. + We assume that 200Mb is enough. My tests failed with 100Mb for + some unknown reason. + """ + try: + fd = os.open(device, os.O_WRONLY) + os.lseek(fd, 513, os.SEEK_SET) + for i in range(200): + os.write(fd, "\0"*1024*1024) + os.close(fd) + + except OSError as e: + if e.errno == 28: # No space left in device + pass + else: + raise LVMError("Falied to zero LVM data on %s." % device) + 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 +303,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 397a648..3c3c4fb 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1692,9 +1692,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. """ @@ -1823,6 +1829,16 @@ 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. + """ + if len(self.pvs) == self.pvCount: + return True + else: + return False + class LVMLogicalVolumeDevice(DMDevice): """ An LVM Logical Volume """ @@ -1922,6 +1938,16 @@ 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. """ + if not self.vg.exists: + return False + + if not self.vg.complete: + return False + return True + 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 b015b55..83015e1 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. @@ -1308,12 +1338,88 @@ 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 + + for parent in vg.parents: + parent.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) + + # remove VG device from list. + self._removeDevice(vg) + + 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.addIgnoredDisk(device.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: + # The user chose not to reinitialize. + # hopefully this will ignore the vg components too. + for lv in device.vg.lvs: + self.addIgnoredDisk(lv.name) + #self._removeDevice(lv) + #self._removeDevice(device.vg) + self.addIgnoredDisk(device.vg.name) + for parent in device.vg.parents: + self.addIgnoredDisk(parent.name) + #self.protectedPartitions.append(parent.name) + return + def populate(self): """ Locate all storage devices. """ # each iteration scans any devices that have appeared since the @@ -1343,6 +1449,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/lvmpv.py b/storage/formats/lvmpv.py index cc243eb..ca11130 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: + lvm.zeroLvmMetadata(self.path) + self.exists = False self.notifyKernel() diff --git a/storage/partitioning.py b/storage/partitioning.py index 8243080..f48d1b2 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -290,6 +290,10 @@ def clearPartitions(storage): parted.PARTITION_LOGICAL): continue + # we will ignore it if its in the ignored disk + if part.name in storage.devicetree._ignoredDisks: + continue + if storage.clearPartType == CLEARPART_TYPE_ALL: clear = True else: -- 1.6.0.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list