Making a VM into template should not be an irreversible process.

shodan

Active Member
Sep 1, 2022
241
67
33
Hi,

After you transformed a VM into a template, your only options are as follows.

1781603347825.png

I suggest that, making a VM into a template should not an irreversible process.

The normal solution to making your template back into a VM.
Is make a full clone, and the only thing that changed is the VM number.

But also, it seems that really needs to be done is to
1. make sure you don't have any linked clones of that template (does it matter ? do LVM snapshot really consider the original "special" ?)
2. remove template: 1 from the config file

So really the UI could have Convert to VM, check for absence of linked VM (if really needed) and change the setting.
 
You can do it on the CLI, just remove the template: 1 from the configuration ... as you wrote

EDIT: Wording, you had it right, I misread on the first answer.
 
Hi @shodan

thanks for posting in the forum!

If you look into the source code f.ex. of the LVMThin Storage Plugin [1], there is a function called create_base which is called during template creation / conversion. This changes properties of the disk into a read only state, as desired for a template.
This is also true for the other storage types and depends on the underlying storage in its functionality.
To revert this state this conversion would have to be reversed.

So while you can force the config to become a regular VM again by changing template back to 0, the storage will not become writable again by itself.

Still if you require this functionality, please feel free to open a Feature request in the Bugzilla [2]

Yours sincerely
Jonas

[1] https://git.proxmox.com/?p=pve-stor...f343fb1800c0c367bd328249328c87568caef;hb=HEAD
[2] https://bugzilla.proxmox.com/
 
I would like to propose the following function to revert the template action, based on create_base

It could be called from such a menu.

1781622051393.png

Code:
sub undo_base {
    my ($class, $storeid, $scfg, $volname) = @_;

    my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
        $class->parse_volname($volname);

    die "undo_base only possible with base image\n" if !$isBase;

    my $vg = $scfg->{vgname};

    my $newname = $name;
    die "unable to derive VM volume name from '$name'\n"
        if $newname !~ s/^base-/vm-/;

    die "refusing unsafe target name '$newname'\n"
        if $newname !~ m/^vm-$vmid-/;

    my $lvs = PVE::Storage::LVMPlugin::lvm_list_volumes($vg);
    my $dat = $lvs->{$vg};

    die "no such volume group '$vg'\n" if !$dat;
    die "no such base volume '$vg/$volname'\n" if !$dat->{$volname};
    die "target volume '$vg/$newname' already exists\n" if $dat->{$newname};

    my $lvinfo = $dat->{$volname};

    die "volume '$vg/$volname' is not an LVM-thin virtual LV\n"
        if ($lvinfo->{lv_type} // '') ne 'V';

    die "volume '$vg/$volname' is not in configured thinpool '$scfg->{thinpool}'\n"
        if ($lvinfo->{pool_lv} // '') ne $scfg->{thinpool};

    # A base volume should normally be inactive after create_base().
    # If it is active, be conservative: it might be manually opened or in use.
    die "refusing to undo active base volume '$vg/$volname'\n"
        if ($lvinfo->{lv_state} // '') eq 'a';

    # Refuse if there are Proxmox-style snapshots of either the current base name
    # or the future vm-* name. The latter avoids confusing stale snapshot names.
    my $snap_re = $PVE::JSONSchema::CONFIGID_RE;

    foreach my $lv (keys %$dat) {
        die "unable to undo base volume - found snapshot '$lv'\n"
            if $lv =~ m/^snap_\Q$volname\E_(?:$snap_re)$/;

        die "unable to undo base volume - found snapshot using target name '$lv'\n"
            if $lv =~ m/^snap_\Q$newname\E_(?:$snap_re)$/;
    }

    # Refuse if any LVM thin snapshot / linked clone has this base LV as origin.
    # This matters because clone_image can create linked clones from base images
    # and the file notes that those relationships are not represented in Proxmox
    # volume IDs.
    my $find_lvm_children = sub {
        my ($vg, $origin_lv) = @_;

        my $children = [];

        my $cmd = [
            '/sbin/lvs',
            '--noheadings',
            '--separator', ':',
            '--options', 'lv_name,origin',
            $vg,
        ];

        run_command(
            $cmd,
            outfunc => sub {
                my $line = shift;

                my ($lv, $origin) = split(/:/, $line, 2);
                $lv = trim($lv // '');
                $origin = trim($origin // '');

                return if !$lv || !$origin;

                # Normalize common LVM display variants.
                $origin =~ s/^\[//;
                $origin =~ s/\]$//;
                $origin =~ s!^/dev/\Q$vg\E/!!;
                $origin =~ s!^\Q$vg\E/!!;

                push @$children, $lv if $origin eq $origin_lv;
            },
            errmsg => "unable to inspect LVM origins in VG '$vg'",
        );

        return [ sort @$children ];
    };

    my $children = $find_lvm_children->($vg, $volname);

    die "unable to undo base volume '$vg/$volname' - it is still the origin of: "
        . join(', ', @$children) . "\n"
        if @$children;

    my $renamed = 0;

    eval {
        my $cmd = ['/sbin/lvrename', $vg, $volname, $newname];
        run_command(
            $cmd,
            errmsg => "lvrename '$vg/$volname' => '$vg/$newname' error",
        );
        $renamed = 1;

        # Reverse create_base(): make it writable and clear activation-skip.
        $cmd = ['/sbin/lvchange', '-prw', '-kn', "$vg/$newname"];
        run_command(
            $cmd,
            errmsg => "lvchange '$vg/$newname' read-write/activationskip error",
        );

        # Match normal Proxmox LVM-thin behavior: do not autoactivate via LVM.
        $set_lv_autoactivation->($vg, $newname, 0);

        # Normal current VM disks are kept active.
        $cmd = ['/sbin/lvchange', '-ay', '-K', "$vg/$newname"];
        run_command(
            $cmd,
            errmsg => "activating LV '$vg/$newname' failed",
        );

        # Final sanity check using fresh LVM data.
        my $post_lvs = PVE::Storage::LVMPlugin::lvm_list_volumes($vg);
        my $post_dat = $post_lvs->{$vg} || {};

        die "undo_base verification failed - old volume '$vg/$volname' still exists\n"
            if $post_dat->{$volname};

        die "undo_base verification failed - new volume '$vg/$newname' does not exist\n"
            if !$post_dat->{$newname};

        die "undo_base verification failed - '$vg/$newname' is not active\n"
            if (($post_dat->{$newname}->{lv_state} // '') ne 'a');
    };

    if (my $err = $@) {
        if ($renamed) {
            my $rollback_err;

            eval {
                # Best-effort rollback to the original base state.
                run_command(
                    ['/sbin/lvchange', '-an', "$vg/$newname"],
                    errmsg => "rollback deactivate '$vg/$newname' error",
                );

                run_command(
                    ['/sbin/lvchange', '-pr', '-ky', "$vg/$newname"],
                    errmsg => "rollback read-only/activationskip '$vg/$newname' error",
                );

                run_command(
                    ['/sbin/lvrename', $vg, $newname, $volname],
                    errmsg => "rollback lvrename '$vg/$newname' => '$vg/$volname' error",
                );
            };

            $rollback_err = $@;

            die "undo_base failed: $err\nrollback also failed: $rollback_err"
                if $rollback_err;
        }

        die $err;
    }

    return $newname;
}
 
You may want to take a look at this information:
https://www.proxmox.com/en/about/open-source/developers
https://www.proxmox.com/en/downloads/proxmox-virtual-environment/agreements

Your proposal will require a Storage API version update, that will affect all existing plugins (internal and external). At the same time you only addressed one built-in plugin - LVM. I'd imagine if such new function was released it would need to cover other built-in storage types, at the very least DIR, Ceph, etc.


Blockbridge : Ultra low latency all-NVME shared storage for Proxmox - https://www.blockbridge.com/proxmox
 
  • Like
Reactions: UdoB