[pve-devel] [PATCH 08/11] add clone_disks

Alexandre Derumier aderumier at odiso.com
Tue Dec 18 10:14:57 CET 2012


qm create --clonefrom vmid --snapname snap

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

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index d2dba3c..e58f8b0 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -78,6 +78,7 @@ my $create_disks = sub {
 	    die "no storage ID specified (and no default storage)\n" if !$storeid;
 	    my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
 	    my $fmt = $disk->{format} || $defformat;
+
 	    my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
 						  $fmt, undef, $size*1024*1024);
 	    $disk->{file} = $volid;
@@ -132,6 +133,84 @@ my $create_disks = sub {
     return $vollist;
 };
 
+my $clone_disks = sub {
+    my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $settings, $snap, $mode) = @_;
+
+    my $vollist = [];
+    my $voliddst = undef;
+
+    my $res = {};
+    PVE::QemuServer::foreach_drive($conf, sub {
+	my ($ds, $disk) = @_;
+
+	my $volid = $disk->{file};
+	if (!$volid || $volid eq 'none' || $volid eq 'cdrom') {
+	    $res->{$ds} = $settings->{$ds};
+	} else{
+
+	    if($mode eq 'linked'){
+
+	        $voliddst = PVE::Storage::volume_clone($storecfg, $volid, $snap, $vmid);
+	        push @$vollist, $voliddst;
+
+	    }elsif($mode eq 'copy'){
+
+		my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+		die "no storage ID specified (and no default storage)\n" if !$storeid;
+
+		my $fmt = undef;
+		if ($volname =~ m/\.(raw|qcow2|vmdk)$/){
+		    $fmt = $1;
+		}
+
+		#target storage is different ? (ex: -virtio0:storeid:fmt)
+		if($settings->{$ds} && $settings->{$ds} =~ m/^(\S+):(raw|qcow2|vmdk)?$/){
+		    ($storeid, $fmt) = ($1, $2);
+		}
+
+		$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
+
+		PVE::Storage::activate_volumes($storecfg, [ $volid ]);
+
+		my ($size) = PVE::Storage::volume_size_info($storecfg, $volid, 1);
+
+		$voliddst = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
+                                                  $fmt, undef, ($size/1024));
+		push @$vollist, $voliddst;
+
+		PVE::Storage::activate_volumes($storecfg, [ $voliddst ]);
+
+		#copy from source
+		PVE::QemuServer::qemu_img_convert($volid, $voliddst, $snap);
+
+	    }
+
+	    $disk->{file} = $voliddst;
+	    $disk->{size} = PVE::Storage::volume_size_info($storecfg, $voliddst, 1);
+
+	    delete $disk->{format}; # no longer needed
+	    $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
+	}
+    });
+
+    # free allocated images on error
+    if (my $err = $@) {
+	syslog('err', "VM $vmid creating disks failed");
+	foreach my $volid (@$vollist) {
+	    eval { PVE::Storage::vdisk_free($storecfg, $volid); };
+	    warn $@ if $@;
+	}
+	die $err;
+    }
+
+    # modify vm config if everything went well
+    foreach my $ds (keys %$res) {
+	$conf->{$ds} = $res->{$ds};
+    }
+
+    return $vollist;
+};
+
 my $check_vm_modify_config_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
 
@@ -256,6 +335,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 => "linked|copy",
+		    type => 'string',
+		    enum => [ 'linked', 'copy' ],
+		    optional => 1,
+		},
+                snapname => get_standard_option('pve-snapshot-name', {
+                    description => "Template Snapshot Name.",
+                    optional => 1,
+                }),
+
 	    }),
     },
     returns => {
@@ -282,6 +376,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();
@@ -298,17 +398,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);
+		    }
 		}
 	    }
 
@@ -374,13 +474,52 @@ __PACKAGE__->register_method({
 	    my $realcmd = sub {
 
 		my $vollist = [];
+		my $conf = undef;
 
-		my $conf = $param;
+		if($clonefrom){
+		    my $parentparam = {};
 
-		eval {
+		    $clonemode = 'linked' if !$clonemode;
+
+		    my $parentconf = PVE::QemuServer::load_config($clonefrom);
+
+		    if($snapname){
+			die "snapshot don't exist" if !$parentconf->{snapshots}->{$snapname};
+			die "You can't clone. vmid $clonefrom snap $snapname is not a template" if !defined($parentconf->{snapshots}->{$snapname}->{template});
+			$parentparam = $parentconf->{snapshots}->{$snapname};
+			delete $parentparam->{snaptime};
+
+		    }else{
 
-		    $vollist = &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $storage);
+			die "fixme:cloning from running is not yet implemented - need to implement live backup feature" if PVE::QemuServer::check_running($clonefrom);
 
+			die "You can't clone. vmid $clonefrom is not a template" if !defined($parentconf->{template});
+			$parentparam = $parentconf;
+		        delete $parentparam->{parent};
+		        delete $parentparam->{snapshots};
+		    }
+
+		    delete $parentparam->{template};
+
+		    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;
+		}else{
+		    $conf = $param;
+		}
+
+		eval {
+		    if($clonefrom){
+			$vollist = &$clone_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $snapname, $clonemode);
+		    }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;
-- 
1.7.10.4




More information about the pve-devel mailing list