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