• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2009 Google Inc. Released under the GPL v2
2
3import re
4
5
6class 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