* 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 (pv_complete): Complete here means that all the related PVs for the containing VG are present in the system. Returns the Completeness value and a list of known devices. * storage/devicelibs/lvm.py (vg_complete): likewise but for VG. * storage/devicelibs/lvm.py (vgreduce): introduces a new argument to the function. rm means use the lvm --removemissing option. * storage/devicetree.py (_handleSysCrapyness): New function intended to catch all unwanted behavior from the system before continuing. * storage/devicetree.py (questionReinitializeIVG): New function intended to ask the user what to do in case we find inconsistent LVM metadata. --- storage/devicelibs/lvm.py | 127 +++++++++++++++++++++++++++++++++++++++++++-- storage/devicetree.py | 104 ++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 5 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 0a19711..df21ea9 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -99,6 +99,22 @@ 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) + os.close(fd) + except: + 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. @@ -229,6 +245,100 @@ def pvinfo(device): return info +# Start of LVM consistency code +# +# PV_{,NOT}_COMPLETE: A PV is complete when it is part of a complete VG or +# it is part of no VG. It is incomplete otherwise. +# VG_{,NOT}_COMPLETE: A VG is complete when all its PVs are accounted for +# by LVM metadata. More specifically, the `lvm vgs` +# command has no PVs described as "unknown device" + +PV_COMPLETE = 0 +PV_NOT_COMPLETE = 1 +VG_COMPLETE = 0 +VG_NOT_COMPLETE = 1 + +def pv_complete(device): + """ Check completeness of lvm structure with Physical Volume device. + + Returns : (state, pv_list) + (None, None) means that device is not a PV. + + state: [PV_COMPLETE | PV_NOT_COMPLETE] + pv_list: List of PVs related to VG. If no related VG is found then return + [device] + """ + # Make sure this is a PV. + args = ["pvs"] + \ + ["--noheadings"] + \ + ["-o", "pv_name"] + \ + [device] + rc = iutil.execWithCapture("lvm", args, + stderr = "/dev/tty5") + if rc == None: + # This is not a PV. + return (None, None) + + # Get the VG name related to the PV. + args = ["pvs"] + \ + ["--noheadings"] + \ + ["-o", "vg_name"] + \ + [device] + rc = iutil.execWithCapture("lvm", args, + stderr = "/dev/tty5") + if rc == None: + # Has no VG, but its ok + # FIXME: should look at stderr. + return (PV_COMPLETE, [device]) + vg_name = rc.strip() + + # Volume Group Completeness (vgs) + (vgc, devices) = vgstatus_ok(vg_name) + if vgc == None: + return (None, None) + elif vgc == VG_NOT_COMPLETE: + return (PV_NOT_COMPLETE, devices) + elif vgc == VG_COMPLETE: + return (PV_COMPLETE, devices) + else: + raise LVMError("Error checking completeness of %s" % vg_name) + +def vgcomplete(vg_name): + """ Check validity of lvm structure with Physical Volume device. + + Returns : (state, pv_list) + (None, None) means that device is not a VG. + + state: [VG_COMPLETE | VG_NOT_COMPLETE] + pv_list: List of PVs related to VG. + """ + # Ask for the names of the PVs. + args = ["vgs"] +\ + ["--noheadings"] + \ + ["-o", "pv_name"] + \ + [vg_name] + rc = iutil.execWithCapture("lvm", args, + stderr = "/dev/tty5") + + if rc == None: + # This is not a VG. + return (None, None) + + # we make sure that every element is striped. Also, we don't want to + # return "unknown device" + pv_names = rc.strip("\n").split("\n") + ret_pv_names = [] + completeness = VG_COMPLETE + for i in range(len(pv_names)): + if pv_names[i].strip() != "unknown device": + ret_pv_names.append(pv_names[i].strip()) + else: + completeness = VG_NOT_COMPLETE + + return (completeness, pv_names) +# +# End of LVM consistency code + def vgcreate(vg_name, pv_list, pe_size): argv = ["vgcreate"] if pe_size: @@ -283,11 +393,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/devicetree.py b/storage/devicetree.py index 66d053e..1b4fcbd 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -136,6 +136,35 @@ def questionInitializeDisk(intf=None, name=None): retVal = True return retVal +def questionReinitializeIVG(intf=None, vg_name=None, pv_names=None): + retVal = False # The less destructive default + if not intf or not pv_names: # We need at least the pv_names (list) + pass + else: + if vg_name is not None: + message_part = _("%s, that is an incomplete" % vg_name) + else: + message_part = _("an unknown") + + rc = intf.messageWindow(_("Warning"), + _("Error processing drive(s) %s.\n" + "It seems that there is inconsistent LVM data " + "(%s) make(s) up %s Volume Group. " + "You can reinitialize all related PVs, which will " + "erase all LVM metadata. Or ignore, which will " + "preserve contents.") + %(str(pv_names), str(pv_names), message_part), + 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. @@ -1296,6 +1325,76 @@ class DeviceTree(object): log.info("setup of %s failed: %s" % (lv_device.name, e)) + def _handleSysCrapyness(self, device): + if device.type == "lvmvg": + # VG completeness (vgc) + (vgc, paths) = lvm.vgcomplete(device.name) + if vgc == lvm.VG_NOT_COMPLETE and \ + questionReinitializeIVG(intf=self.intf, \ + vg_name=device.name, pv_names=paths): + # We reinitialize all de parents. + # First we remove VG data + try: + lvm.vgreduce(device.name, [], rm=True) + lvm.vgremove(device.name) # now we can remove + except LVMError: + # the pvremoves will finish the job. + pass + + for parent in device.parents: + try: + lvm.pvremove(parent.path) + except: + # does lvm.zerometadata make sence? + lvm.zeroLvmMetadata(device.path) + + # Give the device the a default format + kwargs = {"uuid": parent.uuid, + "label": parent.diskLabel, + "device": parent.path, + "exists": parent.exists} + parent.format = formats.getFormat(*[""], **kwargs) + + # to make sure that we have all the devices: + #device.remove(parent.path) + + # Make sure all devices are handled + #for path in devices: + # pass # what to do here? + + # remove VG device from list. + self._removeDevice(device) + + return + + elif device.format.type == "lvmpv" and \ + not lvm.pvstatus_ok(device.path): + if questionReinitializeIVG(intf = self.intf, + vg_name = device.format.vgName, + pv_names = device.path): + # For some reason there is a failure possibility here + # if the lvm commands dont work, just zero the device. + try: + if device.format.vgName is not None: + lvm.vgreduce(device.format.vgName, [], rm=True) + lvm.vgremove(device.format.vgName) # now we can remove + lvm.pvremove(device.path) # finally remove the PV + except LVMError: + # does lvm.zerometadata make sence? + lvm.zeroLvmMetadata(device.path) + + # Give the device the a default format + kwargs = {"uuid": device.uuid, + "label": device.diskLabel, + "device": device.path, + "exists": device.exists} + device.format = formats.getFormat(*[""], **kwargs) + + else: + self.addIgnoredDisk(device.name) + + return + def populate(self): """ Locate all storage devices. """ # each iteration scans any devices that have appeared since the @@ -1325,6 +1424,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._handleSysCrapyness(leaf) + self.teardownAll() def teardownAll(self): -- 1.6.0.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list