[PATCH] Handle system crappyness.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* 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

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux