* storage/devicelibs/lvm.py (pvstatus_ok): New function that tests the status of a PV. If PV part of a complete VG or not part of a VG at all, we consider it to be OK. otherwise it needs to be cleaned. * storage/devicelibs/lvm.py (vgreduce): Add the rm=True arg to the vgreduce call. This will allow us to ignore the device list and tell LVM to clean all the additional LVM metadata. * storage/devicelibs/lvm.py (zeroLvmMetadata): New function to try to clobber LVM metadata completely. This should be used as a last resort and zeros out the device from 512bytes to 10Mb. * storage/devicetree (questionClobberInconsistengVG): New function. Its the callback used to ask the user if the LVM metadata should be erased from a dirty PV. * storage/devicetree (addUdevDevice): Use pvstatus_ok to check for the status of each PV we find. --- storage/devicelibs/lvm.py | 87 +++++++++++++++++++++++++++++++++++++++++++- storage/devicetree.py | 71 ++++++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 0faee10..d4926a6 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -49,6 +49,23 @@ def has_lvm(): return has_lvm +def zeroLvmMetadata(device): + """ This dds from byte 513 to 100Mb of the disk. + + We assume that lvm has nothing to do with the first 512 bytes. + We assume that 20Mb is enough. My tests failed with 10Mb for + some unknown reason. + """ + args = ["if=/dev/zero" , "of=%s"%device] + \ + ["seek=1", "bs=512", "count=400"] + rc = iutil.execWithRedirect("dd", args, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + searchPath=1) + if rc: + raise LVMError("dd failed for %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. @@ -171,6 +188,60 @@ def pvinfo(device): return info +def pvstatus_ok(device): + """ Check validity of lvm structure with Physical Volume device. + + Possibilities: + 1. PV part of a complete VG (OK!) + 2. PV is not part of any VG (OK!) + 3. PV part of an incomplete VG (ERROR!) + 4. device is not a PV (ERROR!) + + Returs True for 1 & 2. False for 3 & 4. + """ + # 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 False + + # 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 True + vg_name = rc.strip() + + # Get all the PVs that are in the VG + args = ["vgs"] + \ + ["--noheadings"] + \ + ["-o", "pv_name"] + \ + [vg_name] + rc = iutil.execWithCapture("lvm", args, + stderr = "/dev/tty5") + pv_names = rc.strip("\n").split("\n") + + if len(pv_names) == 0: + return False + + # Analyse returned PVs + for pv_name in pv_names: + if pv_name.strip() == "unknown device": + return False + + return True + def vgcreate(vg_name, pv_list, pe_size): argv = ["vgcreate"] if pe_size: @@ -212,8 +283,20 @@ def vgdeactivate(vg_name): if rc: raise LVMError("vgdeactivate failed for %s" % vg_name) -def vgreduce(vg_name, pv_list): - rc = iutil.execWithRedirect("lvm", ["vgreduce", 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", stderr = "/dev/tty5", searchPath=1) diff --git a/storage/devicetree.py b/storage/devicetree.py index e974717..fb82e28 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -135,6 +135,35 @@ def questionInitializeDisk(intf=None, name=None): retVal = True return retVal +def questionClobberInconsistengVG(intf=None, pv_name=None, vg_name=None): + retVal = False # The less destructive default + if not intf or not pv_name: + pass + else: + if vg_name is not None: + message_part = _("%s, that is an incomplete" % vg_name) + else: + message_part = _("an unknown") + + message = _("Error processing drive %s.\n" + "It seems that %s is part of %s Volume Group. " + "Or has inconsistent LVM metadata. " + "You can reinitialize %s, loosing all data or ignore " + "it, preserving its contents.") % (pv_name, pv_name, + message_part, pv_name) + + rc = intf.messageWindow(_("Warning"), + message, + type="custom", + custom_buttons = [ _("_Ignore drive"), + _("_Re-initialize drive") ], + 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. @@ -1168,12 +1197,52 @@ class DeviceTree(object): try: vg_name = udev_device_get_vg_name(info) except KeyError: - # no vg name means no vg -- we're done with this pv + # This means either there are valid PVs with no VG. Or, + # leftover PVs from an incomplete VG. + if not lvm.pvstatus_ok(device.path): + if questionClobberInconsistengVG(intf = self.intf, + pv_name = device.path): + lvm.pvremove(device.path) + # Give the device the a default format + for arg in ["vgName", "vgUuid", "peStart"]: + if kwargs.has_key(arg): + del kwargs[arg] + device.format = formats.getFormat(*[""], **kwargs) + + else: + self.ignoredDisks.append(device.name) return + vg_device = self.getDeviceByName(vg_name) if vg_device: vg_device._addDevice(device) + + elif not lvm.pvstatus_ok(device.path): + if questionClobberInconsistengVG(intf = self.intf, + pv_name = device.path, + vg_name = vg_name): + # For some reason there is a failure possibility here + # if the lvm commands dont work, just zero the device. + try: + lvm.vgreduce(vg_name, [], rm=True) #with --removemissing + lvm.vgremove(vg_name) # 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 + for arg in ["vgName", "vgUuid", "peStart"]: + if kwargs.has_key(arg): + del kwargs[arg] + device.format = formats.getFormat(*[""], **kwargs) + + else: + self.ignoredDisks.append(device.name) + + return + else: try: vg_uuid = udev_device_get_vg_uuid(info) -- 1.6.0.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list