Created
March 16, 2017 01:38
-
-
Save parrotmac/1b5641519ed633ecac5b404d8aac6a3b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Great for when Ubuntu Server decides to lock you out because /boot is full of old kernels | |
# Credit: Somebody on Stackoverflow | |
#!/usr/bin/env python2 | |
# See: http://askubuntu.com/questions/345588/what-is-the-safest-way-to-clean-up-boot-partition#answer-430944 | |
import re | |
import sys | |
import subprocess | |
'''Print and execute a system command''' | |
def call(command, stderr=True): | |
print "$ %s\n" % command | |
output = subprocess.check_output(command, stderr=subprocess.STDOUT if stderr else None, shell=True, universal_newlines=True) | |
return output | |
# determine free space on boot | |
disk_space = call('df -k /boot') | |
matched = re.search('^\S+[\t ]+(\d+)\s+(?:\d+\s+){2}(\d+)%\s+(\/[\S]*)$', disk_space, re.M) | |
if not matched: | |
raise SystemExit('Could not determine free space on /boot:\n\n%s' % disk_space) | |
# if /boot is its own partition, save ourselves some calculations | |
if matched.group(3) == '/boot': | |
free_percent = int(matched.group(2)) | |
# otherwise, determine /boot size as % of containing partition | |
else: | |
part_size = matched.group(1) | |
boot_space = call('du -ks /boot') | |
matched = re.search('(\d+)\s+\/boot$', boot_space) | |
free_percent = ( int(part_size) / int(matched.group(1)) ) / 100 | |
# bail unless over X% full (75 by default) | |
min_percent = 75 | |
if len(sys.argv) > 1: | |
try: | |
min_percent = int(sys.argv[1]) | |
except ValueError: | |
pass | |
if free_percent < min_percent: | |
raise SystemExit('Boot partition is only %s%% full' % free_percent) | |
# list all kernels | |
all_kernels = call('dpkg --list "linux-image*"') | |
# get running kernel | |
running_kernel = call('uname -r') | |
# de-dupe kernel versions by assigning as dict keys | |
remove_kernels = dict() | |
for line in all_kernels.split("\n"): | |
# match only installed kernels | |
matched = re.search('^ii\s+linux-image-[a-z-]*([\d.]+-\d+)-.*?$', line) | |
if matched: | |
# skip running kernel | |
if matched.group(1) not in running_kernel: | |
remove_kernels[ matched.group(1) ] = True | |
# sort kernels to remove (as segmented versions) | |
remove_kernels = remove_kernels.keys() | |
remove_kernels.sort(key=lambda s: map(int, re.split('[.-]', s))) | |
for version in remove_kernels: | |
print 'Found: %s\n' % version | |
# if more than two, exclude the last two | |
if len(remove_kernels) > 2: | |
del remove_kernels[-2:] | |
# remove all files for given versions on /boot | |
was_removed = False | |
for version in remove_kernels: | |
# find & print any existing files for version | |
try: | |
to_be_removed = call('ls -1 /boot/*-%s-*' % version) | |
print 'Removing:\n%s' % to_be_removed | |
# fail gracefully if none found | |
except subprocess.CalledProcessError: | |
print 'No files to remove for %s' % version | |
# otherwise remove them with extreme prejudice | |
else: | |
call('rm -rf /boot/*-%s-*' % version) | |
was_removed = True | |
# rebuild grub boot configurations | |
if was_removed: | |
call('update-grub') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment