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