Skip to content

Add tool to remove old, unused kernels#2156

Open
gabjy wants to merge 24 commits intoYunoHost:devfrom
gabjy:kernel-removal-tool
Open

Add tool to remove old, unused kernels#2156
gabjy wants to merge 24 commits intoYunoHost:devfrom
gabjy:kernel-removal-tool

Conversation

@gabjy
Copy link

@gabjy gabjy commented Aug 18, 2025

The problem

We need a tool for the user to remove old kernels

Solution

Adding the kernel removal process to the basic cleanup tool

PR Status

Couldn't test with LXC; works fine on host system

How to test

Running the added code in a Python console

@Josue-T
Copy link
Contributor

Josue-T commented Aug 18, 2025

Great feature. But we might need to ensure that we don't try to remove a running kernel. Because just keeping the last kernel could be not enough if you didn't reboot since the last kernel update. I think that Apt handle this but not sure what will happen when called from dpkg.

Copy link
Member

@alexAubin alexAubin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General principle looks fine to me but I have a bunch of comments 😅 I hope it' not too overwhelming, I tried to provide full explanation for the changes I'm proposing 😅

src/tools.py Outdated
Comment on lines 727 to 731
for kernel in kernels:
if running_kernel[0] not in kernel:
subprocess.run("apt remove -y --purge " + kernel, shell=True)
else:
print("This is the running kernel, it won't be removed")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho we probably want to have a single call to apt using something like below. (I hope list comprehension is not too magic but that's a nice context to learn about it ;P)

Note that I'm not comparing running_kernel to kernel because running_kernel is only the version (as returne by uname -r) and kernel is the apt/dpkg package, something like linux-image-1.2.3-4-amd64

To run a single apt command, i'm using ' '.join(kernels_to_remove) inside an f-string (also not sure if you're familiar with those, I'm a big f-an of them but maybe that's too much magic 😅 )

Suggested change
for kernel in kernels:
if running_kernel[0] not in kernel:
subprocess.run("apt remove -y --purge " + kernel, shell=True)
else:
print("This is the running kernel, it won't be removed")
kernels_to_remove = [kernel for kernel if running_kernel not in kernel]
cmd = f"apt remove -y --purge {' '.join(kernels_to_remove)}"
subprocess.run(cmd, shell=True)

To further improve upon this I also suggest :

  • i would ask the admin for confirmation before actually running the apt, because removing kernels feel a bit touchy ... Something along the lines of "The system is currently running kernel "1.2.3.4". This will remove the following kernels : linux-image-0.1.2, linux-image-0.1.3... Proceed ?" You can use _ask_confirmation(...) for this for which you'll find example usage in the same tools.py
  • we should check the returncode of the subprocess because apt may fail for some reason, in which case we probably want to have some logger.error() message

But this can be iterated upon in a second phase, I hope this ain't too overwheling 😅

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I get what you did here ! Guess I'm not used to Python trick and oneliner yet haha. I'll look into it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the error message and the confirmation as suggested.
Note that I used directly the strings and not the translation framework you're using. Should I create an entry in the locals files or will it be managed automatically by Weblate ?

@alexAubin alexAubin changed the title adding kernel removal to tools_basic_space_cleanup Add tool to remove old, unused kernels Sep 10, 2025
Copy link
Member

@alexAubin alexAubin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after the last remaining comments

gabjy and others added 2 commits September 10, 2025 16:37
Co-authored-by: Alexandre Aubin <[email protected]>
Co-authored-by: Alexandre Aubin <[email protected]>
return
else:
from .utils.app_utils import _ask_confirmation
_ask_confirmation(f"The system is currently running kernel {running_kernel}. This will remove the following kernels : {', '.join(kernels_to_remove)}. Proceed ?")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cf discussion irl : _ask_confirmation in fact expects an i18n key. We can also provide params={ ... } with a dict of values for the i18n string

So let's have something like :

Suggested change
_ask_confirmation(f"The system is currently running kernel {running_kernel}. This will remove the following kernels : {', '.join(kernels_to_remove)}. Proceed ?")
_ask_confirmation(f"tools_clean_old_kernels_confirm", params={"running_kernel": running_kernel, "kernels_to_remove"=', '.join(kernels_to_remove)})

and then you need to edit locales/en.json to add a new entry "tools_clean_old_kernels_confirm" with value :

"The system is currently running kernel {running_kernel}. This will remove the following kernels : {kernels_to_remove}. Proceed ?"

cmd = ["apt", "remove", "-y", "--purge"] + kernels_to_remove
kernel_removal = subprocess.run(cmd)
if kernel_removal.returncode != 0:
logger.error(f"There was an error during the kernels removal. The return code of the 'apt remove' command is : {kernel_removal.returncode}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar i18n consideration here though the syntax is a bit different (oneday™ we will make everything consistent)

Suggested change
logger.error(f"There was an error during the kernels removal. The return code of the 'apt remove' command is : {kernel_removal.returncode}")
logger.error(m18n.n(""tools_clean_old_kernels_error", returncode=kernel_removal.returncode))

and then gotta add another key to en.json tools_clean_old_kernels_error with something like

"There was an error during the kernels removal. The return code of the 'apt remove' command is : {returncode}"

@OniriCorpe
Copy link
Member

i dont want to break the fun but basic-space-cleanup is already doing it...

yunohost tools basic-space-cleanup 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages will be REMOVED:
  libboost-locale1.74.0 libtcl8.6 linux-image-6.1.0-32-amd64
0 upgraded, 0 newly installed, 3 to remove and 0 not upgraded.
After this operation, 416 MB disk space will be freed.
Do you want to continue? [Y/n] 
(Reading database ... 91115 files and directories currently installed.)
Removing libboost-locale1.74.0:amd64 (1.74.0+ds1-21) ...
Removing libtcl8.6:amd64 (8.6.13+dfsg-2) ...
Removing linux-image-6.1.0-32-amd64 (6.1.129-1) ...
/etc/kernel/postrm.d/initramfs-tools:
update-initramfs: Deleting /boot/initrd.img-6.1.0-32-amd64
/etc/kernel/postrm.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.1.0-39-amd64
Found initrd image: /boot/initrd.img-6.1.0-39-amd64
Found linux image: /boot/vmlinuz-6.1.0-38-amd64
Found initrd image: /boot/initrd.img-6.1.0-38-amd64
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done

@alexAubin
Copy link
Member

Ah 😅

Hmmm not sure how it works exactly, on my end it I have version 22, 23, and 28. System is running version 28, but apt autoremove only wants to remove version 22 and keep version 23 ? I guess it wants to keep one previous kernel ?

Still I'm thinking in some case, the /boot/ may be so small it can only contain maybe 2 or 3 kernel, which and you may run out of space just because apt wants to install a more recent kernel

For scenario for servers with a lot of uptime, you may not even be running the latest version already installed because that would involve a reboot, so you may have (version N-1), (version N = the running one), (version N+1 = need to reboot to actually run it) AND apt wants to install "version N+2", so that's 4 versions

@gabjy
Copy link
Author

gabjy commented Nov 2, 2025

Hi, I wanted to add the key for the string inside the _ask_confirmation call inside the en.json file but it can't seem to find it. Should I run a specific command for it to be taken into account ?

@alexAubin
Copy link
Member

Eeeeh nope supposedly it should use the entry from locales/en.json, no need to regen anything 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants