linked clone
------------
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 <aderum...@odiso.com>
---
 PVE/API2/Qemu.pm |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 162 insertions(+), 8 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 63dbd33..0444fed 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -79,6 +79,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;
@@ -133,6 +134,87 @@ my $create_disks = sub {
     return $vollist;
 };
 
+my $clone_disks = sub {
+    my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $settings, $snap, 
$mode, $clonefrom, $running) = @_;
+
+    my $vollist = [];
+    my $voliddst = undef;
+
+    my $res = {};
+    PVE::QemuServer::foreach_drive($conf, sub {
+       my ($ds, $disk) = @_;
+
+       my $volid = $disk->{file};
+       if (PVE::QemuServer::drive_is_cdrom($disk)) {
+           $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
+       } else{
+
+           if($mode eq 'clone'){
+               $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
+               if(!$running){
+                   PVE::QemuServer::qemu_img_convert($volid, $voliddst, $snap);
+               }else{
+                   PVE::QemuServer::qemu_drive_mirror($clonefrom, $ds, 
$voliddst, $vmid);
+               }
+
+           }
+
+           $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) = @_;
 
@@ -257,6 +339,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 => {
@@ -283,6 +380,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();
@@ -299,17 +402,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);
+                   }
                }
            }
 
@@ -372,13 +475,64 @@ __PACKAGE__->register_method({
            my $realcmd = sub {
 
                my $vollist = [];
+               my $conf = undef;
+               my $running = undef;
 
-               my $conf = $param;
+               if($clonefrom){
+                   my $parentparam = {};
 
-               eval {
+                   $running = PVE::QemuServer::check_running($clonefrom);
+
+                   $clonemode = 'clone' 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});
+                       die "clonemode $clonemode feature is not available" if 
!PVE::QemuServer::has_feature($clonemode, $parentconf, $storecfg, $snapname);
+
+
+                       $parentparam = $parentconf->{snapshots}->{$snapname};
+                       delete $parentparam->{snaptime};
+
+                   }else{
 
-                   $vollist = &$create_disks($rpcenv, $authuser, $conf, 
$storecfg, $vmid, $pool, $param, $storage);
+                       die "You can't clone. vmid $clonefrom is not a 
template" if !defined($parentconf->{template}) && !$running;
+                       die "clonemode $clonemode feature is not available" if 
!PVE::QemuServer::has_feature($clonemode, $parentconf, $storecfg);
 
+                       $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;
+                   #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);
+                   }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

_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to