Home | History | Annotate | Download | only in common_lib
      1 # Copyright 2009 Google Inc. Released under the GPL v2
      2 
      3 import re
      4 
      5 
      6 class boottool(object):
      7     """
      8     Common class for the client and server side boottool wrappers.
      9     """
     10 
     11     def __init__(self):
     12         self._xen_mode = False
     13 
     14 
     15     def _run_boottool(self, *options):
     16         """
     17         Override in derivations to execute the "boottool" command and return
     18         the stdout output in case of success. In case of failure an exception
     19         should be raised.
     20 
     21         @param options: a sequence of command line arguments to give to the
     22                 boottool command
     23         @return string with the stdout output of the boottool command.
     24         @raise Exception in case of boottool command failure.
     25         """
     26         raise NotImplementedError('_run_boottool not implemented!')
     27 
     28 
     29     def get_type(self):
     30         """
     31         Return the installed bootloader type.
     32         """
     33         return self._run_boottool('--bootloader-probe').strip()
     34 
     35 
     36     def get_architecture(self):
     37         """
     38         Get the system architecture reported by the bootloader.
     39         """
     40         return self._run_boottool('--arch-probe').strip()
     41 
     42 
     43     def get_titles(self):
     44         """
     45         Returns a list of boot entries titles.
     46         """
     47         return [entry['title'] for entry in self.get_entries().itervalues()]
     48 
     49 
     50     def get_default(self):
     51         """
     52         Return an int with the # of the default bootloader entry.
     53         """
     54         return int(self._run_boottool('--default').strip())
     55 
     56 
     57     def set_default(self, index):
     58         """
     59         Set the default boot entry.
     60 
     61         @param index: entry index number to set as the default.
     62         """
     63         assert index is not None
     64         self._run_boottool('--set-default=%s' % index)
     65 
     66 
     67     def get_default_title(self):
     68         """
     69         Get the default entry title.
     70 
     71         @return a string of the default entry title.
     72         """
     73         return self.get_entry('default')['title']
     74 
     75 
     76     def _parse_entry(self, entry_str):
     77         """
     78         Parse entry as returned by boottool.
     79 
     80         @param entry_str: one entry information as returned by boottool
     81         @return: dictionary of key -> value where key is the string before
     82                 the first ":" in an entry line and value is the string after
     83                 it
     84         """
     85         entry = {}
     86         for line in entry_str.splitlines():
     87             if len(line) == 0:
     88                 continue
     89             name, value = line.split(':', 1)
     90             name = name.strip()
     91             value = value.strip()
     92 
     93             if name == 'index':
     94                 # index values are integrals
     95                 value = int(value)
     96             entry[name] = value
     97 
     98         return entry
     99 
    100 
    101     def get_entry(self, search_info):
    102         """
    103         Get a single bootloader entry information.
    104 
    105         NOTE: if entry is "fallback" and bootloader is grub
    106         use index instead of kernel title ("fallback") as fallback is
    107         a special option in grub
    108 
    109         @param search_info: can be 'default', position number or title
    110         @return a dictionary of key->value where key is the type of entry
    111                 information (ex. 'title', 'args', 'kernel', etc) and value
    112                 is the value for that piece of information.
    113         """
    114         return self._parse_entry(self._run_boottool('--info=%s' % search_info))
    115 
    116 
    117     def get_entries(self):
    118         """
    119         Get all entries information.
    120 
    121         @return: a dictionary of index -> entry where entry is a dictionary
    122                 of entry information as described for get_entry().
    123         """
    124         raw = "\n" + self._run_boottool('--info=all')
    125         entries = {}
    126         for entry_str in raw.split("\nindex"):
    127             if len(entry_str.strip()) == 0:
    128                 continue
    129             entry = self._parse_entry("index" + entry_str)
    130             entries[entry["index"]] = entry
    131 
    132         return entries
    133 
    134 
    135     def get_title_for_kernel(self, path):
    136         """
    137         Returns a title for a particular kernel.
    138 
    139         @param path: path of the kernel image configured in the boot config
    140         @return: if the given kernel path is found it will return a string
    141                 with the title for the found entry, otherwise returns None
    142         """
    143         entries = self.get_entries()
    144         for entry in entries.itervalues():
    145             if entry.get('kernel') == path:
    146                 return entry['title']
    147         return None
    148 
    149 
    150     def add_args(self, kernel, args):
    151         """
    152         Add cmdline arguments for the specified kernel.
    153 
    154         @param kernel: can be a position number (index) or title
    155         @param args: argument to be added to the current list of args
    156         """
    157 
    158         parameters = ['--update-kernel=%s' % kernel, '--args=%s' % args]
    159 
    160         #add parameter if this is a Xen entry
    161         if self._xen_mode:
    162             parameters.append('--xen')
    163 
    164         self._run_boottool(*parameters)
    165 
    166 
    167     def remove_args(self, kernel, args):
    168         """
    169         Removes specified cmdline arguments.
    170 
    171         @param kernel: can be a position number (index) or title
    172         @param args: argument to be removed of the current list of args
    173         """
    174 
    175         parameters = ['--update-kernel=%s' % kernel, '--remove-args=%s' % args]
    176 
    177         #add parameter if this is a Xen entry
    178         if self._xen_mode:
    179             parameters.append('--xen')
    180 
    181         self._run_boottool(*parameters)
    182 
    183 
    184     def __remove_duplicate_cmdline_args(self, cmdline):
    185         """
    186         Remove the duplicate entries in cmdline making sure that the first
    187         duplicate occurances are the ones removed and the last one remains
    188         (this is in order to not change the semantics of the "console"
    189         parameter where the last occurance has special meaning)
    190 
    191         @param cmdline: a space separate list of kernel boot parameters
    192             (ex. 'console=ttyS0,57600n8 nmi_watchdog=1')
    193         @return: a space separated list of kernel boot parameters without
    194             duplicates
    195         """
    196         copied = set()
    197         new_args = []
    198 
    199         for arg in reversed(cmdline.split()):
    200             if arg not in copied:
    201                 new_args.insert(0, arg)
    202                 copied.add(arg)
    203         return ' '.join(new_args)
    204 
    205 
    206     def add_kernel(self, path, title='autoserv', root=None, args=None,
    207                    initrd=None, default=False, position='end',
    208                    xen_hypervisor=None):
    209         """
    210         Add a kernel entry to the bootloader (or replace if one exists
    211         already with the same title).
    212 
    213         @param path: string path to the kernel image file
    214         @param title: title of this entry in the bootloader config
    215         @param root: string of the root device
    216         @param args: string with cmdline args
    217         @param initrd: string path to the initrd file
    218         @param default: set to True to make this entry the default one
    219                 (default False)
    220         @param position: where to insert the new entry in the bootloader
    221                 config file (default 'end', other valid input 'start', or
    222                 # of the title)
    223         @param xen_hypervisor: xen hypervisor image file (valid only when
    224                 xen mode is enabled)
    225         """
    226         if title in self.get_titles():
    227             self.remove_kernel(title)
    228 
    229         parameters = ['--add-kernel=%s' % path, '--title=%s' % title]
    230 
    231         if root:
    232             parameters.append('--root=%s' % root)
    233 
    234         if args:
    235             parameters.append('--args=%s' %
    236                               self.__remove_duplicate_cmdline_args(args))
    237 
    238         if initrd:
    239             parameters.append('--initrd=%s' % initrd)
    240 
    241         if default:
    242             parameters.append('--make-default')
    243 
    244         if position:
    245             parameters.append('--position=%s' % position)
    246 
    247         # add parameter if this is a Xen entry
    248         if self._xen_mode:
    249             parameters.append('--xen')
    250             if xen_hypervisor:
    251                 parameters.append('--xenhyper=%s' % xen_hypervisor)
    252 
    253         self._run_boottool(*parameters)
    254 
    255 
    256     def remove_kernel(self, kernel):
    257         """
    258         Removes a specific entry from the bootloader configuration.
    259 
    260         @param kernel: can be 'start', 'end', entry position or entry title.
    261         """
    262         self._run_boottool('--remove-kernel=%s' % kernel)
    263 
    264 
    265     def boot_once(self, title=None):
    266         """
    267         Sets a specific entry for the next boot, then falls back to the
    268         default kernel.
    269 
    270         @param kernel: title that identifies the entry to set for booting. If
    271                 evaluates to false, this becomes a no-op.
    272         """
    273         if title:
    274             self._run_boottool('--boot-once', '--title=%s' % title)
    275 
    276 
    277     def enable_xen_mode(self):
    278         """
    279         Enables xen mode. Future operations will assume xen is being used.
    280         """
    281         self._xen_mode = True
    282 
    283 
    284     def disable_xen_mode(self):
    285         """
    286         Disables xen mode.
    287         """
    288         self._xen_mode = False
    289 
    290 
    291     def get_xen_mode(self):
    292         """
    293         Returns a boolean with the current status of xen mode.
    294         """
    295         return self._xen_mode
    296