| # we regard the grub file as a preamble, plus a sequence of entry stanzas |
| # starting in 'title'. Whilst probably not entirely accurate, it works |
| # well enough, and is designed not to be lossy |
| |
| import shutil |
| import re |
| import os |
| import os.path |
| import string |
| |
| class grub: |
| config_locations = ['/boot/grub/grub.conf', '/boot/grub/menu.lst', |
| '/etc/grub.conf'] |
| |
| def __init__(self, config_file=None): |
| if config_file: |
| self.config = config_file |
| else: |
| self.config = self.detect() |
| self.read() |
| |
| |
| def read(self): |
| conf_file = file(self.config, 'r') |
| self.lines = conf_file.readlines() |
| conf_file.close() |
| |
| self.entries = [] # list of stanzas |
| self.titles = {} # dictionary of titles |
| entry = grub_entry(-1) |
| count = 0 |
| for line in self.lines: |
| if re.match(r'\s*title', line): |
| self.entries.append(entry) |
| entry = grub_entry(count) |
| count = count + 1 |
| title = line.replace('title ', '') |
| title = title.rstrip('\n') |
| entry.set('title', title) |
| self.titles[title] = entry |
| # if line.startswith('initrd'): |
| if re.match(r'\s*initrd', line): |
| entry.set('initrd', |
| re.sub(r'\s*initrd\s+', '', line)) |
| if re.match(r'\s*kernel', line): |
| entry.set('kernel', |
| re.sub(r'\s*kernel\s+', '', line)) |
| entry.lines.append(line) |
| self.entries.append(entry) |
| self.preamble = self.entries.pop(0) # separate preamble |
| |
| |
| def write(self): |
| conf_file = file(self.config, 'w') |
| conf_file.write(self.preamble) |
| for entry in self.entries: |
| conf_file.write(entry.lines) |
| conf_file.close() |
| |
| |
| def dump(self): |
| for line in self.preamble.lines: |
| print line, |
| for entry in self.entries: |
| for line in entry.lines: |
| print line, |
| |
| def backup(self): |
| shutil.copyfile(self.config, self.config+'.bak') |
| restore = file(autodir + '/var/autotest.boot.restore', 'w') |
| restore.write('cp ' + self.config+'.bak ' + self.config + '\n') |
| restore.close() |
| |
| |
| def bootloader(self): |
| return 'grub' |
| |
| |
| def detect(self): |
| for config in grub.config_locations: |
| if os.path.isfile(config) and not os.path.islink(config): |
| return config |
| |
| |
| def list_titles(self): |
| list = [] |
| for entry in self.entries: |
| list.append(entry.get('title')) |
| return list |
| |
| |
| def print_entry(self, index): |
| entry = self.entries[index] |
| entry.print_entry() |
| |
| |
| def renamed_entry(self, index, newname, args=False): |
| "print a specified entry, renaming it as specified" |
| entry = self.entries[index] |
| entry.set('title', newname) |
| if args: |
| entry.set_autotest_kernel() |
| entry.print_entry() |
| |
| |
| def omit_markers(self, marker): |
| # print, ommitting entries between specified markers |
| print_state = True |
| for line in lines: |
| if line.count(marker): |
| print_state = not print_state |
| else: |
| if print_state: |
| print line |
| |
| |
| def select(self, title, boot_options=None): |
| entry = self.titles[title] |
| print "grub: will boot entry %d (0-based)" % entry.index |
| self.set_default(entry.index) |
| self.set_timeout() |
| |
| |
| def set_default(self, index): |
| lines = (self.preamble).lines |
| for i in range(len(lines)): |
| default = 'default %d' % index |
| lines[i] = re.sub(r'^\s*default.*', |
| default, lines[i]) |
| |
| |
| def set_timeout(self): |
| lines = (self.preamble).lines |
| for i in range(len(lines)): |
| lines[i] = re.sub(r'^timeout.*/', |
| 'timeout 60', lines[i]) |
| lines[i] = re.sub(r'^(\s*terminal .*--timeout)=\d+', |
| r'\1=30', lines[i]) |
| |
| |
| # ---------------------------------------------------------------------- |
| |
| # Note that the lines[] section, whilst fairly foul, is needed to make |
| # sure we preserve the original entry intact with comments, formatting, |
| # and bits we don't understand. |
| |
| class grub_entry: |
| def __init__(self, count): |
| self.lines = [] |
| self.fields = {} # title, initrd, kernel, etc |
| self.index = count |
| |
| |
| def set(self, field, value): |
| print "setting '%s' to '%s'" % (field, value) |
| self.fields[field] = value |
| for i in range(len(self.lines)): |
| m = re.match(r'\s*' + field + r'\s+', self.lines[i]) |
| if m: |
| self.lines[i] = m.group() + value + '\n' |
| |
| |
| def get(self, field): |
| return self.fields[field] |
| |
| |
| def print_entry(self): |
| print self.lines |
| |
| |
| def set_kernel_options(self, options): |
| kernel = self.get('kernel') |
| re.sub(r'(autotest_args:).*', r'\1'+options, kernel) |
| self.set('kernel', kernel) |
| |
| def set_autotest_kernel(self): |
| kernel_words = [] |
| found_path = False |
| # Want to copy most of the entry, replacing the 'path' |
| # part of the entry with vmlinux-autotest in the same |
| # dir, and make sure autotest_args: is (uniquely) added |
| for word in (self.get('kernel')).split(): |
| if word.startswith('--'): |
| kernel_words.append(word) |
| continue |
| if not found_path: |
| word = os.path.dirname(word)+'vmlinuz-autotest' |
| found_path = True |
| if re.match(r'auto(bench|test)_args:', word): |
| break |
| kernel_words.append(word) |
| kernel_words.append('autotest_args: ') |
| self.set('kernel', string.join(kernel_words)) |