[pve-devel] [PATCH 12/13] extend qm create to create clones

Alexandre Derumier aderumier at odiso.com
Tue Jan 29 17:13:52 CET 2013


linked clone (works on template without snapname, works on vm with snapname only)
---------------------------------------------------------------------------------
qm create vmid --clonefrom vmidsrc [--snapname snap] [--clonemode clone]

copy clone : dest storage = source storage
------------------------------------------
qm create vmid --clonefrom vmidsrc [--snapname snap] --clonemode copy

copy clone : dest storage != source storage
------------------------------------------
qm create vmid --clonefrom vmidsrc [--snapname snap] --clonemode copy --virtio0 storeid:[fmt]   (--virtio0 local:raw --virtio1 rbdstorage: --virtio2:nfsstorage:qcow2)

others config params can be add
-------------------------------

qm create vmid --clonefrom vmidsrc [--snapname snap] [--clonemode clone] --memory 2048 --name newvmname

Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/API2/Qemu.pm |  103 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 95 insertions(+), 8 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index ed6da19..eaddcbc 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -357,6 +357,21 @@ __PACKAGE__->register_method({
 		    type => 'string', format => 'pve-poolid',
 		    description => "Add the VM to the specified pool.",
 		},
+                clonefrom => get_standard_option('pve-vmid', {
+                    description => "Template Vmid.",
+                    optional => 1,
+                }),
+		clonemode => {
+		    description => "clone|copy",
+		    type => 'string',
+		    enum => [ 'clone', 'copy' ],
+		    optional => 1,
+		},
+                snapname => get_standard_option('pve-snapshot-name', {
+                    description => "Template Snapshot Name.",
+                    optional => 1,
+                }),
+
 	    }),
     },
     returns => {
@@ -383,6 +398,12 @@ __PACKAGE__->register_method({
 	
 	my $pool = extract_param($param, 'pool');
 
+	my $clonefrom = extract_param($param, 'clonefrom');
+
+	my $clonemode = extract_param($param, 'clonemode');
+
+	my $snapname = extract_param($param, 'snapname');
+
 	my $filename = PVE::QemuServer::config_file($vmid);
 
 	my $storecfg = PVE::Storage::config();
@@ -399,17 +420,17 @@ __PACKAGE__->register_method({
 	if (!$archive) {
 	    &$resolve_cdrom_alias($param);
 
-	    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
+	    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage) if !$clonefrom;
 
 	    &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
-
 	    foreach my $opt (keys %$param) {
 		if (PVE::QemuServer::valid_drivename($opt)) {
 		    my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt});
 		    raise_param_exc({ $opt => "unable to parse drive options" }) if !$drive;
-
-		    PVE::QemuServer::cleanup_drive_path($opt, $storecfg, $drive);
-		    $param->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+		    if(!$clonefrom){
+			PVE::QemuServer::cleanup_drive_path($opt, $storecfg, $drive);
+			$param->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+		    }
 		}
 	    }
 
@@ -472,13 +493,75 @@ __PACKAGE__->register_method({
 	    my $realcmd = sub {
 
 		my $vollist = [];
+		my $conf = undef;
+		my $running = undef;
+		my $parentconf = undef;
+		my $oldconf = undef;
 
-		my $conf = $param;
+		if($clonefrom){
+		    my $parentparam = {};
 
-		eval {
+		    $running = PVE::QemuServer::check_running($clonefrom);
+
+		    $clonemode = 'clone' if !$clonemode;
+
+		    $parentconf = PVE::QemuServer::load_config($clonefrom);
+		    $oldconf = PVE::QemuServer::load_config($clonefrom); #fixme, how to copy the parentconf hashref ?
+
+		    die "you can't clone, the source vm is locked" if $parentconf->{lock};
 
-		    $vollist = &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $storage);
+		    if($snapname){
+			die "snapshot don't exist" if !$parentconf->{snapshots}->{$snapname};
+			die "clonemode $clonemode feature is not available" if !PVE::QemuServer::has_feature($clonemode, $parentconf, $storecfg, $snapname);
 
+			$parentparam = $parentconf->{snapshots}->{$snapname};
+			delete $parentparam->{snaptime};
+
+		    }else{
+
+			die "You can't create a linked clone. vmid $clonefrom is not a full template" if (PVE::QemuServer::is_template($parentconf) != 1 && $clonemode eq 'clone');
+			die "clonemode $clonemode feature is not available" if !PVE::QemuServer::has_feature($clonemode, $parentconf, $storecfg);
+
+			$parentparam = $parentconf;
+		        delete $parentparam->{parent};
+		        delete $parentparam->{snapshots};
+		    }
+
+		    #lock source vm if is not a  template
+		    if (!PVE::QemuServer::is_template($parentconf)){
+			$oldconf->{lock} = 'copy';
+			PVE::QemuServer::update_config_nolock($clonefrom, $oldconf, 1);
+		    }
+
+		    foreach my $opt (keys %$parentparam) {
+			if ($opt =~ m/^net(\d+)$/) {
+			    # add macaddr
+			    my $net = PVE::QemuServer::parse_net($parentparam->{$opt});
+			    $net->{macaddr} =  PVE::Tools::random_ether_addr();
+			    $parentparam->{$opt} = PVE::QemuServer::print_net($net);
+			}
+		    }
+		    $conf = $parentparam;
+		    #add params values excluding disks
+                    foreach my $opt (keys %$param) {
+			next if PVE::QemuServer::parse_drive($opt, $param->{$opt});
+			$conf->{$opt} = $param->{$opt};
+                    }
+
+		}else{
+		    $conf = $param;
+		}
+
+		eval {
+		    if($clonefrom){
+			$vollist = &$clone_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $snapname, $clonemode, $clonefrom, $running);
+			if($oldconf->{lock}){
+			     delete $oldconf->{lock};
+			     PVE::QemuServer::update_config_nolock($clonefrom, $oldconf, 1);
+			}
+		    }else{
+			$vollist = &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $storage);
+		    }
 		    # try to be smart about bootdisk
 		    my @disks = PVE::QemuServer::disknames();
 		    my $firstdisk;
@@ -499,6 +582,10 @@ __PACKAGE__->register_method({
 		my $err = $@;
 
 		if ($err) {
+		    if($oldconf->{lock}){
+			delete $oldconf->{lock};
+			PVE::QemuServer::update_config_nolock($clonefrom, $oldconf, 1);
+		    }
 		    foreach my $volid (@$vollist) {
 			eval { PVE::Storage::vdisk_free($storecfg, $volid); };
 			warn $@ if $@;
-- 
1.7.10.4




More information about the pve-devel mailing list