# Copyright 2009 Google Inc. Released under the GPL v2 import re class boottool(object): """ Common class for the client and server side boottool wrappers. """ def __init__(self): self._xen_mode = False def _run_boottool(self, *options): """ Override in derivations to execute the "boottool" command and return the stdout output in case of success. In case of failure an exception should be raised. @param options: a sequence of command line arguments to give to the boottool command @return string with the stdout output of the boottool command. @raise Exception in case of boottool command failure. """ raise NotImplementedError('_run_boottool not implemented!') def get_type(self): """ Return the installed bootloader type. """ return self._run_boottool('--bootloader-probe').strip() def get_architecture(self): """ Get the system architecture reported by the bootloader. """ return self._run_boottool('--arch-probe').strip() def get_titles(self): """ Returns a list of boot entries titles. """ return [entry['title'] for entry in self.get_entries().itervalues()] def get_default(self): """ Return an int with the # of the default bootloader entry. """ return int(self._run_boottool('--default').strip()) def set_default(self, index): """ Set the default boot entry. @param index: entry index number to set as the default. """ assert index is not None self._run_boottool('--set-default=%s' % index) def get_default_title(self): """ Get the default entry title. @return a string of the default entry title. """ return self.get_entry('default')['title'] def _parse_entry(self, entry_str): """ Parse entry as returned by boottool. @param entry_str: one entry information as returned by boottool @return: dictionary of key -> value where key is the string before the first ":" in an entry line and value is the string after it """ entry = {} for line in entry_str.splitlines(): if len(line) == 0: continue name, value = line.split(':', 1) name = name.strip() value = value.strip() if name == 'index': # index values are integrals value = int(value) entry[name] = value return entry def get_entry(self, search_info): """ Get a single bootloader entry information. NOTE: if entry is "fallback" and bootloader is grub use index instead of kernel title ("fallback") as fallback is a special option in grub @param search_info: can be 'default', position number or title @return a dictionary of key->value where key is the type of entry information (ex. 'title', 'args', 'kernel', etc) and value is the value for that piece of information. """ return self._parse_entry(self._run_boottool('--info=%s' % search_info)) def get_entries(self): """ Get all entries information. @return: a dictionary of index -> entry where entry is a dictionary of entry information as described for get_entry(). """ raw = "\n" + self._run_boottool('--info=all') entries = {} for entry_str in raw.split("\nindex"): if len(entry_str.strip()) == 0: continue entry = self._parse_entry("index" + entry_str) entries[entry["index"]] = entry return entries def get_title_for_kernel(self, path): """ Returns a title for a particular kernel. @param path: path of the kernel image configured in the boot config @return: if the given kernel path is found it will return a string with the title for the found entry, otherwise returns None """ entries = self.get_entries() for entry in entries.itervalues(): if entry.get('kernel') == path: return entry['title'] return None def add_args(self, kernel, args): """ Add cmdline arguments for the specified kernel. @param kernel: can be a position number (index) or title @param args: argument to be added to the current list of args """ parameters = ['--update-kernel=%s' % kernel, '--args=%s' % args] #add parameter if this is a Xen entry if self._xen_mode: parameters.append('--xen') self._run_boottool(*parameters) def remove_args(self, kernel, args): """ Removes specified cmdline arguments. @param kernel: can be a position number (index) or title @param args: argument to be removed of the current list of args """ parameters = ['--update-kernel=%s' % kernel, '--remove-args=%s' % args] #add parameter if this is a Xen entry if self._xen_mode: parameters.append('--xen') self._run_boottool(*parameters) def __remove_duplicate_cmdline_args(self, cmdline): """ Remove the duplicate entries in cmdline making sure that the first duplicate occurances are the ones removed and the last one remains (this is in order to not change the semantics of the "console" parameter where the last occurance has special meaning) @param cmdline: a space separate list of kernel boot parameters (ex. 'console=ttyS0,57600n8 nmi_watchdog=1') @return: a space separated list of kernel boot parameters without duplicates """ copied = set() new_args = [] for arg in reversed(cmdline.split()): if arg not in copied: new_args.insert(0, arg) copied.add(arg) return ' '.join(new_args) def add_kernel(self, path, title='autoserv', root=None, args=None, initrd=None, default=False, position='end', xen_hypervisor=None): """ Add a kernel entry to the bootloader (or replace if one exists already with the same title). @param path: string path to the kernel image file @param title: title of this entry in the bootloader config @param root: string of the root device @param args: string with cmdline args @param initrd: string path to the initrd file @param default: set to True to make this entry the default one (default False) @param position: where to insert the new entry in the bootloader config file (default 'end', other valid input 'start', or # of the title) @param xen_hypervisor: xen hypervisor image file (valid only when xen mode is enabled) """ if title in self.get_titles(): self.remove_kernel(title) parameters = ['--add-kernel=%s' % path, '--title=%s' % title] if root: parameters.append('--root=%s' % root) if args: parameters.append('--args=%s' % self.__remove_duplicate_cmdline_args(args)) if initrd: parameters.append('--initrd=%s' % initrd) if default: parameters.append('--make-default') if position: parameters.append('--position=%s' % position) # add parameter if this is a Xen entry if self._xen_mode: parameters.append('--xen') if xen_hypervisor: parameters.append('--xenhyper=%s' % xen_hypervisor) self._run_boottool(*parameters) def remove_kernel(self, kernel): """ Removes a specific entry from the bootloader configuration. @param kernel: can be 'start', 'end', entry position or entry title. """ self._run_boottool('--remove-kernel=%s' % kernel) def boot_once(self, title=None): """ Sets a specific entry for the next boot, then falls back to the default kernel. @param kernel: title that identifies the entry to set for booting. If evaluates to false, this becomes a no-op. """ if title: self._run_boottool('--boot-once', '--title=%s' % title) def enable_xen_mode(self): """ Enables xen mode. Future operations will assume xen is being used. """ self._xen_mode = True def disable_xen_mode(self): """ Disables xen mode. """ self._xen_mode = False def get_xen_mode(self): """ Returns a boolean with the current status of xen mode. """ return self._xen_mode