[pve-devel] [PATCH 1/4] qm copy implementation

Alexandre Derumier aderumier at odiso.com
Mon Oct 29 17:23:45 CET 2012


This add

qm copy <vmid> <vmiddest>

This duplicate vmid config, regenerate mac address, and copy disks for new vm.

default destination storeid is the same than source storeid.
default format (if file) is the same than source format.

destinations storage and file format can be override with

qm copy <vmid> <vmiddest> -virtio0 local:qcow2 -virtio1 sheepdog: -virtio2 nfs:raw  -virtio3 rbd:

Current implement don't copy snapshots, and copy disks from "you are here" state

Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/API2/Qemu.pm |  150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qm               |    2 +
 2 files changed, 152 insertions(+)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index f4ae566..246a476 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -418,6 +418,156 @@ __PACKAGE__->register_method({
     }});
 
 __PACKAGE__->register_method({
+    name => 'copy_vm',
+    path => '',
+    method => 'POST',
+    description => "Copy a virtual machine.",
+    permissions => {
+	description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. If you create disks you need 'Datastore.AllocateSpace' on any used storage.",
+	check => [ 'or', 
+		   [ 'perm', '/vms/{vmiddest}', ['VM.Allocate']],
+		   [ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param => 'pool'],
+	    ],
+    },
+    protected => 1,
+    proxyto => 'node',
+    parameters => {
+    	additionalProperties => 0,
+	properties => PVE::QemuServer::json_config_properties(
+	    {
+		node => get_standard_option('pve-node'),
+		vmid => get_standard_option('pve-vmid'),
+		vmiddest => get_standard_option('pve-vmid'),
+                snapname => get_standard_option('pve-snapshot-name', {
+                    description => "Snapshot Name.",
+                    optional => 1,
+                }),
+		pool => { 
+		    optional => 1,
+		    type => 'string', format => 'pve-poolid',
+		    description => "Add the VM to the specified pool.",
+		},
+	    }),
+    },
+    returns => {
+	type => 'string',
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $rpcenv = PVE::RPCEnvironment::get();
+
+	my $authuser = $rpcenv->get_user();
+
+	my $node = extract_param($param, 'node');
+
+	my $vmid = extract_param($param, 'vmid');
+
+	my $vmiddest = extract_param($param, 'vmiddest');
+
+	my $snapname = extract_param($param, 'snapname');
+
+	my $pool = extract_param($param, 'pool');
+
+	my $filename = PVE::QemuServer::config_file($vmiddest);
+
+	my $storecfg = PVE::Storage::config();
+
+	my $conf = PVE::QemuServer::load_config($vmid);
+
+	PVE::Cluster::check_cfs_quorum();
+
+	if (defined($pool)) {
+	    $rpcenv->check_pool_exist($pool);
+	} 
+
+
+    	&$resolve_cdrom_alias($param);
+	&$check_vm_modify_config_perm($rpcenv, $authuser, $vmiddest, $pool, [ keys %$param]);
+
+	if($snapname && !$conf->{snapshots}->{$snapname}){
+		die "snapshot don't exist";
+	}elsif($snapname && $conf->{snapshots}->{$snapname}){
+    
+	    $conf = $conf->{snapshots}->{$snapname};
+	    delete $conf->{snaptime};
+	}
+	delete $conf->{parent};
+	delete $conf->{snapshots};
+
+	$conf->{name} .= "-copy";
+
+	die "VM $vmid is running\n" if PVE::QemuServer::check_running($vmid) && !$snapname;
+
+	foreach my $opt (keys %$conf) {
+	    if ($opt =~ m/^net(\d+)$/) {
+		# add macaddr
+		my $net = PVE::QemuServer::parse_net($conf->{$opt});
+		$net->{macaddr} =  PVE::Tools::random_ether_addr();
+		$conf->{$opt} = PVE::QemuServer::print_net($net);
+	    }
+	}
+
+	my $addVMtoPoolFn = sub {		       
+	    my $usercfg = cfs_read_file("user.cfg");
+	    if (my $data = $usercfg->{pools}->{$pool}) {
+		$data->{vms}->{$vmiddest} = 1;
+		$usercfg->{vms}->{$vmiddest} = $pool;
+		cfs_write_file("user.cfg", $usercfg);
+	    }
+	};
+
+        my $createfn = sub {
+
+            # test after locking
+            die "unable to create vm $vmiddest: config file already exists\n"
+                if -f $filename;
+
+            my $realcmd = sub {
+
+                my $vollist = [];
+
+                eval {
+
+                    $vollist = &$copy_disks($rpcenv, $authuser, $conf, $storecfg, $vmiddest, $pool, $param, $snapname);
+
+                    # try to be smart about bootdisk
+                    my @disks = PVE::QemuServer::disknames();
+                    my $firstdisk;
+                    foreach my $ds (reverse @disks) {
+                        next if !$conf->{$ds};
+                        my $disk = PVE::QemuServer::parse_drive($ds, $conf->{$ds});
+                        next if PVE::QemuServer::drive_is_cdrom($disk);
+                        $firstdisk = $ds;
+                    }
+
+                    if (!$conf->{bootdisk} && $firstdisk) {
+                        $conf->{bootdisk} = $firstdisk;
+                    }
+
+                    PVE::QemuServer::update_config_nolock($vmiddest, $conf);
+
+                };
+                my $err = $@;
+
+                if ($err) {
+                    foreach my $volid (@$vollist) {
+                        eval { PVE::Storage::vdisk_free($storecfg, $volid); };
+                        warn $@ if $@;
+                    }
+                    die "copy failed - $err";
+                }
+
+                PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
+            };
+
+            return $rpcenv->fork_worker('qmcopy', $vmiddest, $authuser, $realcmd);
+        };
+
+	return PVE::QemuServer::lock_config_full($vmiddest, 1, $createfn);
+    }});
+
+__PACKAGE__->register_method({
     name => 'vmdiridx',
     path => '{vmid}',
     method => 'GET',
diff --git a/qm b/qm
index 25f84ea..3a8712e 100755
--- a/qm
+++ b/qm
@@ -409,6 +409,8 @@ my $cmddef = {
 
     create => [ "PVE::API2::Qemu", 'create_vm', ['vmid'], { node => $nodename }, $upid_exit ],
 
+    copy => [ "PVE::API2::Qemu", 'copy_vm', ['vmid', 'vmiddest'], { node => $nodename }, $upid_exit ],
+
     destroy => [ "PVE::API2::Qemu", 'destroy_vm', ['vmid'], { node => $nodename }, $upid_exit ],
 
     migrate => [ "PVE::API2::Qemu", 'migrate_vm', ['vmid', 'target'], { node => $nodename }, $upid_exit ],
-- 
1.7.10.4




More information about the pve-devel mailing list