A template is protected config + volumes. Can be current config or snapshot.
template_create: -we lock volume if storage need it for clone (files or rbd) -then we add template:1 to the config (current or snapshot) template_delete: - We need to check if clones of volumes exist before remove the template - We unlock the storage - we remove template:1 from the config (current of snapshot) Signed-off-by: Alexandre Derumier <aderum...@odiso.com> --- PVE/API2/Qemu.pm | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ PVE/QemuServer.pm | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qm | 2 ++ 3 files changed, 158 insertions(+) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 487fde2..91cf569 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -2298,4 +2298,82 @@ __PACKAGE__->register_method({ return $rpcenv->fork_worker('qmdelsnapshot', $vmid, $authuser, $realcmd); }}); +__PACKAGE__->register_method({ + name => 'template', + path => '{vmid}/template', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Create a Template.", + permissions => { + check => ['perm', '/vms/{vmid}', [ 'VM.Template' ]], + }, + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + snapname => get_standard_option('pve-snapshot-name', { + optional => 1, + }), + delete => { + optional => 1, + type => 'boolean', + }, + }, + }, + returns => { type => 'null'}, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $authuser = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $snapname = extract_param($param, 'snapname'); + + my $delete = extract_param($param, 'delete'); + + my $updatefn = sub { + + my $conf = PVE::QemuServer::load_config($vmid); + + PVE::QemuServer::check_lock($conf); + + if($delete){ + + PVE::QemuServer::template_delete($vmid, $conf, $snapname); + + if($snapname){ + die "snapshot '$snapname' don't exist\n" if !defined($conf->{snapshots}->{$snapname}); + delete $conf->{snapshots}->{$snapname}->{template}; + }else{ + delete $conf->{template}; + } + + }else{ + + PVE::QemuServer::template_create($vmid, $conf, $snapname); + + if($snapname){ + die "snapshot '$snapname' don't exist\n" if !defined($conf->{snapshots}->{$snapname}); + $conf->{snapshots}->{$snapname}->{template} = 1; + }else{ + $conf->{template} = 1; + } + } + + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + }; + + PVE::QemuServer::lock_config($vmid, $updatefn); + return undef; + }}); + + + 1; diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index a34c89f..1e70c3a 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -181,6 +181,12 @@ my $confdesc = { description => "Lock/unlock the VM.", enum => [qw(migrate backup snapshot rollback)], }, + template => { + optional => 1, + type => 'boolean', + description => "Current config or snapshot is a template", + default => 0, + }, cpulimit => { optional => 1, type => 'integer', @@ -4411,4 +4417,76 @@ sub has_feature{ return 1 if !$err; } + +sub template_create { + my ($vmid, $conf, $snapname) = @_; + + die "snapshot $snapname don't exist" if ($snapname && (!defined($conf->{snapshots}->{$snapname}))); + + die "you can't create a template without snapshot on a running vm" if check_running($vmid) && !$snapname; + + die "you can't create a template from a snapshot with a vmstate" if $snapname && $conf->{snapshots}->{$snapname}->{vmstate}; + + $conf = $conf->{snapshots}->{$snapname} if $snapname; + + my $storecfg = PVE::Storage::config(); + + foreach_drive($conf, sub { + my ($ds, $drive) = @_; + + return if drive_is_cdrom($drive); + + my $volid = $drive->{file}; + my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); + if ($storeid) { + my $scfg = PVE::Storage::storage_config($storecfg, $storeid); + PVE::Storage::volume_protect($storecfg, $volid, $snapname, 1); + } + }); + +} + +sub template_delete { + my ($vmid, $conf, $snapname) = @_; + + $conf = $conf->{snapshots}->{$snapname} if $snapname; + my $storecfg = PVE::Storage::config(); + + #search if clone child of template disks exist + foreach_drive($conf, sub { + my ($ds, $drive) = @_; + + return if drive_is_cdrom($drive); + + my $volid = $drive->{file}; + my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); + $volname .= "@".$snapname if $snapname; + + if ($storeid) { + my $dl = PVE::Storage::vdisk_list($storecfg, $storeid, undef); + PVE::Storage::foreach_volid($dl, sub { + my ($volumeid, undef, $info) = @_; + if($info->{parent} && $info->{parent} eq $volname) { + die "You can't delete this template. $volumeid is a clone of this template"; + } + }); + } + }); + + #unprotect the parent file or parent snapshot + foreach_drive($conf, sub { + my ($ds, $drive) = @_; + + return if drive_is_cdrom($drive); + + my $volid = $drive->{file}; + my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); + if ($storeid) { + my $scfg = PVE::Storage::storage_config($storecfg, $storeid); + PVE::Storage::volume_protect($storecfg, $volid, $snapname); + } + }); + +} + 1; diff --git a/qm b/qm index 76bc229..93f621c 100755 --- a/qm +++ b/qm @@ -379,6 +379,8 @@ my $cmddef = { rollback => [ "PVE::API2::Qemu", 'rollback', ['vmid', 'snapname'], { node => $nodename } , $upid_exit ], + template => [ "PVE::API2::Qemu", 'template', ['vmid'], { node => $nodename }], + start => [ "PVE::API2::Qemu", 'vm_start', ['vmid'], { node => $nodename } , $upid_exit ], stop => [ "PVE::API2::Qemu", 'vm_stop', ['vmid'], { node => $nodename }, $upid_exit ], -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel