Proxmox subscription nag removal
This is a description of the ‘No valid subscription’ popup removal method - the kind of content which Proxmox stubbornly keep censoring from their own forum. The purposely bad UX design - essentially nagware - is something that no user of free software should ever need to endure.
For the purpose of demonstration, we take a manual approach first - effect of which would, however, sooner or later be inevitably lost due to upgrades overwriting the modified component. Therefore, we will also automate our modification - in a more robust way than could be otherwise achieved through the use of regular sed
-based methods.
Note
The actual removal method is applicable to PVE, as well as PBS and PMG as it is part of Proxmox Widget Toolkit that is shared amongst the three.
Tip
You are also welcome to use a ready-made tool for streamlining Proxmox setups with no subscription. It will do everything described below - keep reading if interested in the innards - and much more.
Prerequisites
First, we make sure that correct repositories for upgrades have been set up and - unless we have a good reason not to, assume already up to date system.
Important
All actions below preferably performed over direct SSH connection or console, NOT via Web GUI.
Manual removal
We will simply remove a snippet of code in a single JavaScript file.
First, let’s make a backup copy of it:
cp /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js{,.bak}
Use your favourite editor and look for checked_command
function around line 600:
checked_command: function(orig_cmd) {
Proxmox.Utils.API2Request(
{
url: '/nodes/localhost/subscription',
method: 'GET',
failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
success: function(response, opts) {
- let res = response.result;
- if (res === null || res === undefined || !res || res
- .data.status.toLowerCase() !== 'active') {
- Ext.Msg.show({
- title: gettext('No valid subscription'),
- icon: Ext.Msg.WARNING,
- message: Proxmox.Utils.getNoSubKeyHtml(res.data.url),
- buttons: Ext.Msg.OK,
- callback: function(btn) {
- if (btn !== 'ok') {
- return;
- }
- orig_cmd();
- },
- });
- } else {
orig_cmd();
- }
},
},
);
},
The lines highlighted in red are to be removed, so that - on success - there is no extra logic to bring up the popup.
Note
There are other methods out there, but rather than resorting to elegant one-liner, we will remove everything that is not meant to ever get into the execution path. This will also serve as a more reliable starting point when it comes to automation.
After we have performed the change and saved the file, there is actually no need to restart anything on the host. Instead, a browser refresh (CTRL
+F5
) will do.
If things go wrong
Besides we made a backup prior, which we can just put back, there is also the fool-proof method or simply reinstalling the stock file with the whole original package:
apt reinstall proxmox-widget-toolkit
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
0 upgraded, 0 newly installed, 1 reinstalled, 0 to remove and 0 not upgraded.
Need to get 220 kB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 http://download.proxmox.com/debian/pve bookworm/pve-no-subscription amd64 proxmox-widget-toolkit all 4.3.3 [220 kB]
Fetched 220 kB in 0s (723 kB/s)
(Reading database ... 53687 files and directories currently installed.)
Preparing to unpack .../proxmox-widget-toolkit_4.3.3_all.deb ...
Unpacking proxmox-widget-toolkit (4.3.3) over (4.3.3) ...
Setting up proxmox-widget-toolkit (4.3.3) ...
How to automate it
Since the file we have just modified is likely to get repeatedly overwritten during periods of UI development, we want to automate it, so that we can then follow with it after each upgrade.
The primary issue is not as much that the code itself keeps changing, but that it keeps moving around, so a regular patch
command would be ineffective here - as it relies on matching line numbers.
Instead, we make use of Perl (which is part of PVE stack) and follows the exact same steps for the predictable and safe outcome as the manual method did. Unlike other scripts available, it does NOT risk partial matches of other (unintended) parts of code in the future and their inadvertent removal, it also contains the exact copy of the JavaScript to be seen in context.
Script
The actual script is the single line at the bottom, the rest is just our before and after blocks.
Caution
It is highly likely that the actual spacing and formatting would have changed meanwhile, do NOT simply copy and paste the entire snippet below, paste in your own blocks from the original JavaScript file.
#!/usr/bin/perl -pi.bak
my $orig = quotemeta << 'EOF';
checked_command: function(orig_cmd) {
Proxmox.Utils.API2Request(
{
url: '/nodes/localhost/subscription',
method: 'GET',
failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
success: function(response, opts) {
let res = response.result;
if (res === null || res === undefined || !res || res
.data.status.toLowerCase() !== 'active') {
Ext.Msg.show({
title: gettext('No valid subscription'),
icon: Ext.Msg.WARNING,
message: Proxmox.Utils.getNoSubKeyHtml(res.data.url),
buttons: Ext.Msg.OK,
callback: function(btn) {
if (btn !== 'ok') {
return;
}
orig_cmd();
},
});
} else {
orig_cmd();
}
},
},
);
},
EOF
my $repl = << 'EOF';
checked_command: function(orig_cmd) {
Proxmox.Utils.API2Request(
{
url: '/nodes/localhost/subscription',
method: 'GET',
failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
success: function(response, opts) {
orig_cmd();
},
},
);
},
EOF
BEGIN { undef $/; } s/$orig/$repl/;
Shebang
arguments provide for execution of the script over input, sed
-style (-p
), and also guarantee a backup copy is retained (-i.bak
).
Original pattern ($orig
)and its replacement ($repl
) are assigned to variables using HEREDOC
notation in full, the original gets non-word characters escaped (quotemeta
) for use with regular expressions.
The entire replacement is in a single shot on multi-line (undef $/;
) pattern, where original is substituted for replacement (s/$orig/$repl/;
). This also means that if a perfect match is not found, nothing is modified.
Use
Save the code above into a file, e.g.: no-nag.pl
The script can be run with no execute rights pointing at the JavaScript library:
perl no-nag.pl /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
Verify
Result can be confirmed by comparing the backed up and the in-place modified file:
diff -u /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js{.bak,}
--- /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak
+++ /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
@@ -560,24 +560,7 @@
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
success: function(response, opts) {
- let res = response.result;
- if (res === null || res === undefined || !res || res
- .data.status.toLowerCase() !== 'active') {
- Ext.Msg.show({
- title: gettext('No valid subscription'),
- icon: Ext.Msg.WARNING,
- message: Proxmox.Utils.getNoSubKeyHtml(res.data.url),
- buttons: Ext.Msg.OK,
- callback: function(btn) {
- if (btn !== 'ok') {
- return;
- }
- orig_cmd();
- },
- });
- } else {
orig_cmd();
- }
},
},
);
Maintenance
It would be convenient to create our own wrapper script that executes this script following each upgrade:
#!/bin/bash
apt update && \
apt full-upgrade && \
perl /path/to/script/no-nag.pl /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
After all, the way it is designed is to not match anything in the worst case, so if it becomes out of date, nothing will be broken.
Tip
The above mentioned dedicated no subscription tool uses more sophisticated method which means that there is no need to remember to run it with upgrades in any custom fashion.
Excuse limited formatting, absent referencing and missing media content.
Your feedback is welcome in the main GitHub repository.