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