[PATCH] (master) Fix grub stage1 installation for /boot on md raid1.

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

 



From: David Cantrell <dcantrell@xxxxxxxxxx>

This is a patch to fix installation of grub in case we have md raid 1 /boot
device. I posted similar patch some time ago, Hans reviewed it, but it
didn't gather much attention so I was afraid to push it - it changes behavior.
Now I decided to do things a bit differently.  Also, some weeks ago Hans has
pushed a patch fixing bootloader target selection for mdraid (commit
45a7048e5f56316e052e4699b5ec70aa291ddd5e) upon which my patch is standing.  So
here I come again (sorry for repeating myself).

We offer installation into mbr or boot partition (/dev/mdX) in UI. In F11 and RHEL5
if md boot partition was on /dev/sda1 and /dev/sdb1, in case of "mbr" we
installed grub twice into mbr of /dev/sda, and in case of "partition" into mbr
of /dev/sda and /dev/sdb. Member drive removal in the second case didn't work
I think (no boot).

Hans's patch changed the behavior (it was expected) - we are really installing
bootloader into boot device if /dev/mdX is selected, and if mbr is selected,
we are installing twice into mbr of selected drive as before.

My patch wants to make it behave "the right" way, that is - install where you
were asked to, and be able to boot when one member disk is removed (see bug
https://bugzilla.redhat.com/show_bug.cgi?id=213578), so:

A) In case of installing into boot partition: install it there in a way that the
boot works if one member is removed.

B) In case of installing into mbr: iff the disk contains member of boot md
array, install also into mbrs of other members. If one member is removed, be
able to boot - this is fixed by the patch.

Note: I'd like to fix upgrade of grub part too, especially when now the
behavior has changed wrt what we write out in grub.conf and
/etc/sysconfig/grub. Also UI can writing for mbr could say that we'd
install into mbr of other member disks too (if it is the case)

If you want details, below are some cases that illustrate the change (also the
code may be hard to follow for given case, I can post a test script using
which you can get resulting grub input for given case)

1) sda, sdb, install to mbr, /boot is sdb1

works both with and without the patch, the same grub commands are run

2) sda, sdb, sdc, install to mbr of sda, /boot is mdarray of sdb3 and sdc3

without the patch:
- boots with both disks, doesn't if any is removed
- log:
        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd0,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,2)/grub/grub.conf
        grub> Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd1,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd1,2)/grub/grub.conf
        grub>
         both disks - OK
         2nd disk removed - OK
         1st disk removed - OK

with the patch:
- boots even when any of member disks is removed
- log:

        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd1,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd1,2)/grub/grub.conf
        grub>

3) sda, sdb, install to mbr of sda, /boot is mdarray of sda3 and sdb3

without patch:
- it boots with both disks, doesn't find stage1 (_ on screen) if any of them
  is removed
- log:
        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd1,1)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd1,1)/grub/grub.conf
        grub> Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd2,1)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd2,1)/grub/grub.conf
        grub>

with the patch:
- boots even when one of member disks is removed
- log:
        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> device (hd0) /dev/sdb
        grub> root (hd0,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,2)/grub/grub.conf
        grub> Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd0,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,2)/grub/grub.conf
        grub>

4) like 3), but /boot is mdarray of sda3 and sdb2 - notice different partition

numbers - removing a specific one of member disks can't work as location of
grub.conf file stored in first block of stage2 is shared and so contains only
the last grub-installed value.

without the patch:
- doesn't boot with both disks, doesn't boot with any single disk
- log:

        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd0,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,2)/grub/grub.conf
        grub> Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd1,1)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd1,1)/grub/grub.conf
        grub>

with the patch:
- boots when one second disk is removed, not when the first one is removed
  (in the sense of boot order)
- log:
        Running... ['/sbin/grub-install', '--just-copy']
        Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> device (hd0) /dev/sdb
        grub> root (hd0,1)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,1)/grub/grub.conf
        grub> Running... ['/sbin/grub', '--batch', '--no-floppy', '--device-map=/boot/grub/device.map']

            GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

         [ Minimal BASH-like line editing is supported.  For the first word, TAB
           lists possible command completions.  Anywhere else TAB lists the possible
           completions of a device/filename.]
        grub> root (hd0,2)
         Filesystem type is ext2fs, partition type 0xfd
        grub> install --stage2=/boot/grub/stage2 /grub/stage1 d (hd0) /grub/stage2 p (hd0,2)/grub/grub.conf
        grub>
---
 booty/bootloaderInfo.py |    2 +-
 booty/x86.py            |  125 +++++++++++++++++++++++++++++++++++------------
 2 files changed, 95 insertions(+), 32 deletions(-)

diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py
index f67083f..4919cf3 100644
--- a/booty/bootloaderInfo.py
+++ b/booty/bootloaderInfo.py
@@ -628,7 +628,7 @@ class efiBootloaderInfo(bootloaderInfo):
                                     stderr = "/dev/tty5")
         return rc
 
-    def installGrub(self, instRoot, bootDevs, grubTarget, grubPath,
+    def installGrub(self, instRoot, bootDev, grubTarget, grubPath,
                     target, cfPath):
         if not iutil.isEfi():
             raise EnvironmentError
diff --git a/booty/x86.py b/booty/x86.py
index ececf0f..69b6b86 100644
--- a/booty/x86.py
+++ b/booty/x86.py
@@ -99,34 +99,106 @@ class x86BootloaderInfo(efiBootloaderInfo):
             if rc:
                 return rc
 
-    def installGrub(self, instRoot, bootDevs, grubTarget, grubPath,
+    def matchingBootTargets(self, stage1Devs, bootDevs):
+        matches = []
+        for stage1Dev in stage1Devs:
+            for mdBootPart in bootDevs:
+                if getDiskPart(stage1Dev, self.storage)[0] == getDiskPart(mdBootPart, self.storage)[0]:
+                    matches.append((stage1Dev, mdBootPart))
+        return matches
+
+    def addMemberMbrs(self, matches, bootDevs):
+        updatedMatches = list(matches)
+        bootDevsHavingStage1Dev = [match[1] for match in matches]
+        for mdBootPart in bootDevs:
+            if mdBootPart not in bootDevsHavingStage1Dev:
+               updatedMatches.append((getDiskPart(mdBootPart, self.storage)[0], mdBootPart))
+        return updatedMatches
+
+    def installGrub(self, instRoot, bootDev, grubTarget, grubPath,
                     target, cfPath):
         if iutil.isEfi():
-            return efiBootloaderInfo.installGrub(self, instRoot, bootDevs, grubTarget,
+            return efiBootloaderInfo.installGrub(self, instRoot, bootDev, grubTarget,
                                                  grubPath, target, cfPath)
 
         args = "--stage2=/boot/grub/stage2 "
 
-        for bootDev in bootDevs:
-            cmds = []
-            gtPart = self.getMatchingPart(bootDev, grubTarget)
-            gtDisk = self.grubbyPartitionName(getDiskPart(gtPart, self.storage)[0])
-            bPart = self.grubbyPartitionName(bootDev)
-            cmd = "root %s\n" % (bPart,)
+        stage1Devs = self.getPhysicalDevices(grubTarget)
+        bootDevs = self.getPhysicalDevices(bootDev.name)
 
-            stage1Target = gtDisk
-            if target == "partition":
-                stage1Target = self.grubbyPartitionName(gtPart)
+        installs = [(None,
+                     self.grubbyPartitionName(stage1Devs[0]),
+                     self.grubbyPartitionName(bootDevs[0]))]
+
+        if bootDev.type == "mdarray":
+
+            matches = self.matchingBootTargets(stage1Devs, bootDevs)
+
+            # If the stage1 target disk contains member of boot raid array (mbr
+            # case) or stage1 target partition is member of boot raid array
+            # (partition case)
+            if matches:
+                # 1) install stage1 on target disk/partiton
+                stage1Dev, mdMemberBootPart = matches[0]
+                installs = [(None,
+                             self.grubbyPartitionName(stage1Dev),
+                             self.grubbyPartitionName(mdMemberBootPart))]
+                firstMdMemberDiskGrubbyName = self.grubbyDiskName(getDiskPart(stage1Dev, self.storage)[0])
+
+                # 2) and install stage1 on other members' disks/partitions too
+                # NOTES:
+                # - the goal is to be able to boot after a members' disk removal
+                # - so we have to use grub device names as if after removal
+                #   (i.e. the same disk name (e.g. (hd0)) for both member disks)
+                # - if member partitions have different numbers only removal of
+                #   specific one of members will work because stage2 containing
+                #   reference to config file is shared and therefore can contain
+                #   only one value
+
+                # if target is mbr, we want to install also to mbr of other
+                # members, so extend the matching list
+                matches = self.addMemberMbrs(matches, bootDevs)
+                for stage1Target, mdMemberBootPart in matches[1:]:
+                    # prepare special device mapping corresponding to member removal
+                    mdMemberBootDisk = getDiskPart(mdMemberBootPart, self.storage)[0]
+                    # It can happen due to ks --driveorder option, but is it ok?
+                    if not mdMemberBootDisk in self.drivelist:
+                        continue
+                    mdRaidDeviceRemap = (firstMdMemberDiskGrubbyName,
+                                         mdMemberBootDisk)
+
+                    stage1TargetGrubbyName = self.grubbyPartitionName(stage1Target)
+                    rootPartGrubbyName = self.grubbyPartitionName(mdMemberBootPart)
+
+                    # now replace grub disk name part according to special device
+                    # mapping
+                    old = self.grubbyDiskName(mdMemberBootDisk).strip('() ')
+                    new = firstMdMemberDiskGrubbyName.strip('() ')
+                    rootPartGrubbyName = rootPartGrubbyName.replace(old, new)
+                    stage1TargetGrubbyName = stage1TargetGrubbyName.replace(old, new)
+
+                    installs.append((mdRaidDeviceRemap,
+                                     stage1TargetGrubbyName,
+                                     rootPartGrubbyName))
+
+                # This is needed for case when /boot member partitions have
+                # different numbers. Shared stage2 can contain only one reference
+                # to grub.conf file, so let's ensure that it is reference to partition
+                # on disk which we will boot from - that is, install grub to
+                # this disk as last so that its reference is not overwritten.
+                installs.reverse()
 
+        cmds = []
+        for mdRaidDeviceRemap, stage1Target, rootPart in installs:
+            if mdRaidDeviceRemap:
+                cmd = "device (%s) /dev/%s\n" % tuple(mdRaidDeviceRemap)
+            else:
+                cmd = ''
+            cmd += "root %s\n" % (rootPart,)
             cmd += "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \
-                (args, grubPath, stage1Target, grubPath, bPart, grubPath)
+                (args, grubPath, stage1Target, grubPath, rootPart, grubPath)
             cmds.append(cmd)
-
-            rc = self.runGrubInstall(instRoot, bootDev, cmds, cfPath)
-            if rc:
-                return rc
-
-        return 0
+        return self.runGrubInstall(instRoot, bootDev.name, cmds, cfPath)
 
     def writeGrub(self, instRoot, bl, kernelList, chainList,
             defaultDev, justConfigFile):
@@ -165,7 +237,7 @@ class x86BootloaderInfo(efiBootloaderInfo):
             f.write("#          all kernel and initrd paths are relative "
                     "to /boot/, eg.\n")
         except KeyError:
-            bootDev = self.storage.rootDevice
+            bootDev = rootDev
             grubPath = "/boot/grub"
             cfPath = "/boot/"
             f.write("# NOTICE:  You do not have a /boot partition.  "
@@ -347,20 +419,11 @@ class x86BootloaderInfo(efiBootloaderInfo):
         f.close()
             
         if not justConfigFile:
-            return self.installGrub(instRoot, bootDevs, grubTarget, grubPath,
-                                    target, cfPath)
+            return self.installGrub(instRoot, bootDev, grubTarget, grubPath,
+                             target, cfPath)
 
         return 0
 
-    def getMatchingPart(self, bootDev, target):
-        bootName, bootPartNum = getDiskPart(bootDev, self.storage)
-        devices = self.getPhysicalDevices(target)
-        for device in devices:
-            name, partNum = getDiskPart(device, self.storage)
-            if name == bootName:
-                return device
-        return devices[0]
-
     def grubbyDiskName(self, name):
         return "hd%d" % self.drivelist.index(name)
 
-- 
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