Commits

Moises Henriquez committed 3ec405a Merge

Merged in m0e_lnx/m0e-vinstall (pull request #10)

Fix Lilo OS detection problems and possible current root as install target

Comments (0)

Files changed (3)

vinstall/backend/bootloader.py

 from vinstall.backend import utils, media
 from vinstall.backend import sp
 import unittest
-
+from vinstall.core import log
 
 LILO = object()
 GRUB = object()
 GRUB2 = object()
 
+LOG = log.get_logger("bootloader_backend")
+
+class LiloDotConfEntry(object):
+    """Dummy wrapper for reading a lilo.conf.  Has absolutely no use at all.
+    The only reason it exists is to do some unittesting"""
+    root = None
+    label = None
+
+    @classmethod
+    def all(cls, fpath):
+	entries = []
+	with open(fpath) as data:
+	    for line in data:
+		line = line.strip()
+		if line.startswith("root") or \
+		  line.startswith("other"):
+		    entry = cls()
+		    entry.root = line.split("=")[-1].strip()
+		    yield entry
 
 class Lilo(object):
     """LiLo bootloader
 
     """
     def __init__(self, target, default_os=None, timeout=3,
-            vga_mode="788"):
+            vga_mode="788", bootloader_root=None):
+	"""
+	arguments:
+		target: target device or partition where lilo will be installed
+		default_os: default os to be booted after the timeout value if the user does not take action
+		timeout: N in seconds the boot menu should be displayed
+		vga_mode: vga mode used for boot menu and while booting
+		bootloader_root: Path to root partition containing the bootloader config
+		
+	"""
 
         self.target = target
         self.default_os = default_os
         self.timeout = None
         self.set_timeout(timeout)
         self.buffer = []
-        self.lilo_root = utils.get_mounted("/")
-        tamu = "/boot/tamu"
-        if not os.path.exists(tamu):
-            os.mkdir(tamu)
+	if not bootloader_root:
+            self.lilo_root = utils.get_mounted("/")
+	else:
+	    self.lilo_root = bootloader_root
+	self.bootloader_root = bootloader_root
+        self.tamu = "/boot/tamu"
+        if not os.path.exists(self.tamu):
+            os.mkdir(self.tamu)
+
 
     def add_os(self, system):
+	"""Add the information about this operating system to the
+	class"""
         if system.type == "linux":
             self.add_unix_os(system)
-        elif "microsoft" in system.type:
+        elif "chain" in system.type:
             self.add_ms_os(system)
         else:
             #XXX
             pass
 
+    def copy_unix_bootdata(self, system):
+	"""Copy the kernel and initrd to our partition
+	if necessary"""
+	
+	umount = False
+        if system.type != "linux":
+	    return
+	if system.os_root != self.bootloader_root:
+	    # Mount the os root partition if needed
+	    partition = media.Partition(system.root)
+	    if not partition.is_mounted():
+		umount = True
+		if not os.path.exists(partition.mountpoint):
+		    os.mkdir(partition.mountpoint)
+		mountpoint = partition.mount(partition.mountpoint)
+	    else:
+		mountpoint = partition.mountpoint
+
+	    # Copy the kernel to the local tamu path
+	    kernel_from = mountpoint + system.kernel #XX: os.path.join wont work here!!
+	    kernel_to = system.tamu_kernel_path
+
+	    if not os.path.exists(os.path.split(kernel_to)[0]):
+	        os.mkdir(os.path.split(kernel_to)[0])
+	    shutil.copy2(kernel_from, kernel_to)
+
+	    # Copy the initrd to the local tamu path
+	    if system.initrd:
+	        initrd_from = mountpoint + system.initrd #XX: os.path.join wont work here !!
+		if not os.path.exists(initrd_from):
+		    LOG.debug("WARNING:  reported initrd for %s (%s) does not exist"% (
+			system.label, system.initrd))
+		else:
+		    shutil.copy2(initrd_from, system.tamu_initrd_path)
+
+        # un-mount the partition if necessary
+	if umount:
+	    partition.umount()
+
     def add_unix_os(self, system):
         """Add an operating system to the boot menu
 
         """
         if system in self.operating_systems:
             raise RuntimeError("OS already added")
-        if system.root != self.lilo_root:
-
-            # mount os root partition if needed
-            umount = False
-            partition = media.Partition(system.root)
-            if not partition.is_mounted():
-                mountpoint = system.root.replace("/dev/", "/mnt/")
-                if not os.path.exists(mountpoint):
-                    os.mkdir(mountpoint)
-                partition.mount(mountpoint)
-                umount = True
-
-            # copy kernel
-            kernel_from = os.path.join(partition.mountpoint, "boot",
-                    system.kernel_name)
-            kernel_dest = os.path.join("/boot/tamu", system.kernel)
-            shutil.copy2(kernel_from, kernel_dest)
-
-            # copy initrd if needed
-            if system.initrd:
-                initrd_from = os.path.join(partition.mountpoint,
-                        system.initrd)
-                initrd_dest = os.path.join("/boot/tamu",
-                        system.initrd)
-                if os.path.exists(initrd_from) and \
-                        not os.path.exists(initrd_dest):
-                    shutil.copy2(initrd_from, initrd_dest)
-
-            # umount if needed
-            if umount:
-                partition.umount()
-
-            # write config lines
-            tab = " " * 4
-            self.buffer.append("# -- %s on %s --" % (system.label,
-                system.root))
-            self.buffer.append("image = %s" % system.kernel)
-            self.buffer.append("%s label = %s" % (tab, system.label))
-            if system.initrd:
-                self.buffer.append("%s initrd = %s" % (tab,
-                    system.initrd))
-#            if system.appendline:
-#                if "splash" not in system.appendline:
-#                    self.buffer.append('%s append = "%s splash quiet"' %
-#                            (tab, system.appendline))
-#                else:
-#                    self.buffer.append('%s append = "%s"' % (tab,
-#                            system.appendline))
-	    if system.appendline:
-	        self.buffer.append('%s append = "%s"'% (tab, system.appendline))
-            self.buffer.append("%s read-only" % tab)
-            self.buffer.append("# --")
-
-            self.operating_systems.append(system)
+
+        # write config lines
+        tab = " " * 4
+        self.buffer.append("# -- %s on %s --" % (system.label,
+            system.root))
+	
+	if system.root != self.bootloader_root:
+	    self.buffer.append("image = %s"% system.tamu_kernel_path)
+	    self.buffer.append("%s label = %s"% (tab, system.tamu_lilo_label))
+	    if system.initrd:
+	        self.buffer.append("%s initrd = %s"% (tab, system.tamu_initrd_path))
+	else:
+	    self.buffer.append("image = %s"% system.kernel)
+	    self.buffer.append("%s label = %s"% (tab, system.label))
+	    if system.initrd:
+		self.buffer.append("%s initrd = %s"% ( tab, system.initrd))
+	self.buffer.append("%s root = %s" % (tab, system.root))
+        if system.appendline:
+	    self.buffer.append('%s append = "%s"'% (tab, system.appendline))
+        self.buffer.append("%s read-only" % tab)
+        self.buffer.append("# --")
+
+        self.operating_systems.append(system)
 
     def add_ms_os(self, system):
         """Add MS os to boot menu
 
         """
-        self.buffer.append("# -- %s on %s --" % (system.label, system.root))
+	tab = " " * 4
+        self.buffer.append("# -- %s on %s --" % (system.long_desc, system.root))
         self.buffer.append("other = %s" % system.root)
-        self.buffer.append("label = %s" % system.label)
-        self.buffer.append("table = %s" % system.root[:-1])
+        self.buffer.append("%s label = %s" % (tab, system.label))
+        self.buffer.append("%s table = %s" % (tab, system.root[:-1]))
+	self.operating_systems.append(system)
         
     def set_default_os(self, system):
         """Set the default OS
 
         """
         newline = "\n"
+	if self.default_os:
+	    default_os_line = "default = %s"% self.default_os.label
+	else:
+	    default_os_line = "#"
         header = (
             "# LILO configuration file.",
             "# ",
             "# Generated by VASM.",
             "# ",
             "boot = %s" % self.target,
+	    default_os_line,
             "compact",
             "prompt",
             "timeout = %s" % self.timeout,
         """Install the bootloader
 
         """
+	for ops in self.operating_systems:
+            self.copy_unix_bootdata(ops)
         return sp.check_call(["/sbin/lilo"])
 
     def test_install(self, config_file, bootdev):
     """
     type = None         # str indicating OS type
     kernel = None       # Kernel image
+    initrd = None	# Initial Ram Disk
     root = None         # path to OS root
     appendline = None   # append line for lilo.conf
 
     def kernel_name(self):
         return os.path.split(self.kernel)[-1]
 
+    @property
+    def initrd_name(self):
+	if self.initrd:
+	    return os.path.split(self.initrd)[-1]
+	return None
+
+    @property
+    def os_root(self):
+	return self.root
+
+    @property
+    def _short_root(self):
+	return os.path.split(self.root)[-1]
+
+    @property 
+    def tamu_kernel_path(self):
+	return os.path.join("/boot/tamu/", "%s-%s"% (self.kernel_name, self._short_root))
+
+    @property
+    def tamu_initrd_path(self):
+	rootpart = os.path.split(self.os_root)[-1]
+	return os.path.join("/boot/tamu/","%s-%s"% ( self.initrd_name, self._short_root))
+
+    @property
+    def tamu_lilo_label(self):
+	"""Return a lilo-friendly label for an OS on a foreign partition
+	"""
+	return "%s-%s"% (self.label, self._short_root)
+
     @classmethod
-    def all(cls, include_running_os=False):
+    def all(cls, include_running_os=True, op_data = None, bp_data = None,
+            bootsplash_disabled = False, current_root = None):
         """Return one OperatingSystem instance for each OS
         installed
+        Arguments:
+            include_running_os:  True or False (self explanatory)
+            op_data: os-prober data (for unittests only)
+            bp_data: linux-boot-prober data (for unittests only)
+            bootsplash_disabled: True or False (self explanatory)
+            current_root: media.Partition object indicating the current partition mounted at /
 
         """
-        os_prober = sp.check_output(["os-prober", "&>/dev/null"])
-        found_os = os_prober.split("\n")
-
-        if include_running_os and os.path.exists('/boot/vmlinuz'):
-            running_os = cls()
-            running_os.kernel = '/boot/vmlinuz'
-            running_os.label = 'VectorLinux'
-            running_os.type = "linux"
-            if os.path.exists('/boot/initrd'):
-                running_os.initrd = '/boot/initrd'
-            running_os.root = utils.get_mounted("/")
-            yield running_os
+	if not op_data:
+            op_data = sp.check_output(["os-prober", "&>/dev/null"])
+        found_os = op_data.split("\n")
+        
+        systems = []
+        if include_running_os and current_root is not None:
+            myroot = current_root.path()
+            LOG.debug("Creating Lilo boot entries for current OS")
+            LOG.debug("Current root=%s"% myroot)
+            if os.path.exists('/boot/vmlinuz'):
+                current_os = cls()
+                current_os.initrd = None
+                current_os_tui = cls()
+                current_os_tui.initrd = None
+                current_os.kernel = '/boot/vmlinuz'
+                current_os.label = 'Vector'
+                current_os.type = 'linux'
+                if os.path.exists('/boot/initrd'):
+                    current_os.initrd = '/boot/initrd'
+                    current_os_tui.initrd = '/boot/initrd'
+                current_os.root = myroot
+                current_os_tui.label = 'Vector-TUI'
+                current_os_tui.kernel = '/boot/vmlinuz'
+                current_os_tui.root = myroot
+                current_os_tui.type = "linux"
+                if bootsplash_disabled:
+                    current_os.appendline = "verbose"
+                else:
+                    current_os.appendline = "quiet splash"
+                current_os_tui.appendline = "2 verbose"
+                systems.append(current_os)
+                systems.append(current_os_tui)
 
         for i in found_os:
             if ":" not in i:
             root, long_desc, short_desc, os_type = \
                     [ s.strip() for s in i.split(":") ]
             operating_system = cls()
+            operating_system.initrd = None
+	    LOG.debug("Found %s on %s"% (os_type, root))
             if short_desc.startswith("Vector"):
                 short_desc = "VectorLinux - %s"% root.split("/")[-1]
-            operating_system.label = short_desc
+            if "vector" in long_desc.lower():
+		if "64" in long_desc:
+		    operating_system.label = "VLocity"
+		else:
+		    operating_system.label = "Vector"
+            else:
+                operating_system.label = short_desc
             if os_type.lower() == "linux":
                 operating_system.type = "linux"
-                bootinfo = get_linux_boot_info(root)
+                bootinfo = get_linux_boot_info(root, bp_data)
                 if bootinfo is None:
                     continue
-                operating_system.root = bootinfo["root"] or root
+                operating_system.root = bootinfo["root"] #XX or root ??
                 operating_system.kernel = bootinfo["kernel"]
                 operating_system.initrd = bootinfo["initrd"]
-                operating_system.appendline = bootinfo["append"]
+                #XX: The bootinfo["append"] reports invalid stuff that may cause LiLO to fail
+                #XX: Setting appendline to None until further notice.
+                operating_system.appendline = None #: bootinfo["append"]
             elif "microsoft" in os_type.lower() or \
-                    "windows" in os_type.lower():
+                    "windows" in os_type.lower() or \
+		    "chain" in os_type.lower():
+		        operating_system.long_desc = long_desc
+			operating_system.short_desc = short_desc
                         operating_system.root = root
-                        operating_system.label = root.split("/")[-1]
-                        operating_system.type = "windows"
-            #XXX what about bsd, osx, etc
+			if "microsoft" in short_desc.lower() or \
+			   "windows" in short_desc.lower():
+			    operating_system.label = "Windows-%s"% os.path.split(root)[-1]
+			else:
+			    operating_system.label = "Other_OS-%s"% os.path.split(root)[-1] 
+                        operating_system.type = "chain"
+            #XXX what about bsd, osx, etc.  Will 'chain' cover them?
             else:
                 continue
-            yield operating_system
-
+            systems.append(operating_system)
+	
+	for unix in systems:
+	    if unix.type.lower() == "linux":
+		yield unix
+	for other in systems:
+	    if other.type.lower() != "linux":
+		yield other
 
 class MasterBootRecord(object):
     """The MBR of a device
             return None
 
 
-def get_linux_boot_info(root_partition):
+def get_linux_boot_info(root_partition, data = None):
     """Use linux-boot-prober to find out how to boot this OS
 
     /dev/sdb1:/dev/sdb1::/boot/vmlinuz26:/boot/kernel26.img:root=/dev/sdb1
 
     """
-    proc = sp.check_output(['linux-boot-prober', root_partition])
-    hits = proc.split("\n")
+    if not data:
+        data = sp.check_output(['linux-boot-prober', root_partition])
+    else:
+	data = "\n".join(
+	    [ x for x in data.split("\n") if root_partition in x])
+    hits = data.split("\n")
     if not hits:
         return None
     line = hits[0]
     line = line.strip()
     root, boot, _, kernel, initrd, append = \
             [ i.strip() for i in line.split(":") ]
-    return dict(root=root, kernel=kernel, initrd=initrd, append=append)
-
+    #XX: linux-boot-prober is reporting hits and misses for some of these
+    #XX: boot parameters.  Lets verify that the kernel and initrd are correct
+    p = media.Partition(root_partition)
+    umount = False
+    if not p.is_mounted():
+        mountpoint = p.mount(p.mountpoint)
+        umount = True
+    else:
+        mountpoint = p.mountpoint
+    if not os.path.exists(mountpoint + initrd):
+        LOG.debug("Reported initrd %s for OS in %s does not exist."% \
+                  (mountpoint + initrd, root_partition))
+        initrd = ""
+        ramdisks = ("boot/initrd","boot/initrd.gz","initrd","initrd.img",
+                    "boot/initrd.img")
+        for rd in ramdisks:
+            if os.path.exists(os.path.join(mountpoint, rd)):
+                initrd = os.path.join("/",rd)
+                LOG.debug("Located correct path to initrd for os on %s as %s"% \
+                          (root_partition, initrd))
+                break
+    else:
+        LOG.debug("Path to initrd for os on %s verified"% root_partition)
+
+    if umount:
+        p.umount()
+        
+    res = dict(root=root, kernel=kernel, initrd=initrd, append=append)
+    return res
 
 class FakeDeviceTestCase(unittest.TestCase):
 
         f = os.fdopen(fd)
         f.seek(140000)
         os.write(fd, "0")
+	self.op_data = "\n".join(
+		("/dev/sda1:Windows 7 (loader):Windows:chain",
+		"/dev/sda2:VLocity 7.1 B2:Linux:linux",
+		"/dev/sdb1:VectorLinux 7.1 B1.1:Linux:linux",
+		"/dev/sdb2:Slackware Linux 14.1:Linux:linux"))
+	self.bp_data = "\n".join(
+	    ("/dev/sda2:/dev/sda2::/boot/vmlinuz:/boot/initrd:root=/dev/sda2",
+		"/dev/sdb1:/dev/sdb1::/boot/vmlinuz:/boot/initrd:root=/dev/sdb1",
+		"/dev/sdb2:/dev/sdb2::/boot/vmlinuz:/boot/initrd:root=/dev/sdb2"))
 
     def tearDown(self):
         os.unlink(self.path)
 class LiloTestCase(FakeDeviceTestCase):
 
     def test_lilo(self):
-        if not os.path.exists('/bin/lilo'):
+        if not os.path.exists('/sbin/lilo'):
             return #XXX 
         lilo = Lilo(self.path)
-        for ops in OperatingSystem.all(include_running_os=True):
+        for ops in OperatingSystem.all(include_running_os=True,
+	    op_data = self.op_data, bp_data = self.bp_data):
             lilo.add_os(ops)
         lilo.write_config(config_path="/tmp/lilo-test")
-        lilo.test_install("/tmp/lilo-test", self.path)
-        mbr = MasterBootRecord(self.path)
-        self.assertEqual(mbr.bootloader(), LILO)
+	# Test to make sure the entries got written to the config file
+	found = [ entry.root for entry in LiloDotConfEntry.all('/tmp/lilo-test')]
+	written = [ x.split(':')[0] for x in self.op_data.split("\n")]
+	for i in written:
+	    self.assertIn(i, found)
+#        lilo.test_install("/tmp/lilo-test", self.path)
+#        mbr = MasterBootRecord(self.path)
+#        self.assertEqual(mbr.bootloader(), LILO)
 
 
 class Grub2TestCase(FakeDeviceTestCase):
 
     def test_grub2(self):
         return #XXX
-        grub2 = Grub2(self.path)
+        grub2 = Grub2(self.path, data = self.testdata)
         grub2.write_config(config_path="/tmp/grub2-test")
         grub2.test_install("/tmp/grub2-test", self.path)
         mbr = MasterBootRecord(self.path)

vinstall/controller/bootloader.py

 from vinstall.core import Render, Controller, model, log
 
 
-LOG = log.get_logger("bootloader")
+LOG = log.get_logger("bootloader_controller")
 
 
 class Bootloader(Controller):
 
         """
         LOG.debug("Bootloader: %s", bootloader)
-        LOG.debug("Target; %s", target)
+        LOG.debug("Target: %s", target)
         yield self.set_bind_mounts, tuple(), "Mounting pseudo filesystems"
         yield self.depmod, tuple(), "Running depmod"
         yield self.mkinitrd, tuple(), "Creating initrd"
 
         """
         LOG.debug("Installing LILO to %s", target)
-        lilo = bootloader.Lilo(target)
-        for system in bootloader.OperatingSystem.all():
-	    LOG.debug("Adding %s entry to LiLo boot menu"% system.label)
-	    if system.root == self.config['target_device'].path():
-	        # Set grab the values for the tui entry here.
-	        tui_label = system.label
-   	        tui_kernel = system.kernel
-	    if splash_opt == True: 
-		# Splash was disabled... override the initd line for the entry
-	        if system.root == self.config['target_device'].path():
-		    system.initrd = None
-		    system.appendline = "root=%s verbose"% system.root
-	    else:
-	        system.appendline = "root=%s splash quiet"% system.root
-            lilo.add_os(system)
-	if tui_kernel:
-	    # Create the TUI entry for LiLO
-	    LOG.debug("Adding Linux-tui entry to LiLO boot menu")
-	    tui = bootloader.OperatingSystem()
-	    rootpart = self.config['target_device']
-	    tui.kernel = tui_kernel
-	    tui.root = rootpart.path()
-	    tui.type = "linux"
-	    tui.initrd = None
-	    tui.label = "%s-tui"%tui_label
-	    tui.appendline = 'root=%s ro 2 verbose'% tui.root
-	    lilo.add_os(tui)
-	
-
-        with utils.Chroot("/mnt/TARGET"):
-            lilo.backup_config()
-            lilo.write_config()
-            lilo.install()
+        # Set the current root.  We would normally use utils.get_mounted("/") for this
+        # but on a new install, that returns None, so we must give it the
+        # self.config['target_device'] object itself.
+	with utils.Chroot("/mnt/TARGET"):
+            current_root = self.config['target_device'].path()
+	    lilo = bootloader.Lilo(target = target,
+		                   bootloader_root = current_root)
+
+            for system in bootloader.OperatingSystem.all(
+                    include_running_os = True,
+                    bootsplash_disabled = splash_opt,
+                    current_root = self.config['target_device']):
+		lilo.add_os(system)
+		LOG.debug("Adding LiLO boot menu item for %s from %s"% \
+			(system.label, system.root))
+		if system.root == current_root and "-TUI" not in system.label:
+		    LOG.debug("Setting %s from %s as the default boot OS"% \
+			(system.label, system.root))
+		    lilo.set_default_os(system)
+
+	    lilo.backup_config()
+	    lilo.write_config()
+	    lilo.install()
 
     def install_grub(self, target):
         """ Install Grub2 to target

vinstall/controller/mountpoints.py

 from vinstall.backend.media import Partition
 from vinstall.backend import mountpoints
 import os
-
+from vinstall.backend.utils import get_mounted
 
 class Mountpoint(Controller):
     """Setup target locations
     def init(self):
         self.config["mountpoints"] = []
         source = self.config["install_media"].device.device_path
-        self.partitions = [ p for p in Partition.all() if p.device_path != source ]
+	self.partitions = []
+	current_root = get_mounted("/")
+	for p in Partition.all():
+	    if p.device_path not in (source, current_root):
+		self.partitions.append(p)
 
     def render(self):
         """Show mount options