[pve-devel] [PATCH 4/5] extend qm create to create clones
Alexandre Derumier
aderumier at odiso.com
Mon Apr 29 08:41:04 CEST 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 8d81f43..479d116 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -354,6 +354,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 => {
@@ -380,6 +395,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();
@@ -396,17 +417,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);
+ }
}
}
@@ -469,13 +490,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 "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};
+ }
+ delete($conf->{template});
+
+ }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;
@@ -496,6 +579,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