[pve-devel] [PATCH 1/4] qm copy implementation
Alexandre Derumier
aderumier at odiso.com
Fri Oct 26 14:32:01 CEST 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 | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
qm | 2 +
2 files changed, 139 insertions(+)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index f4ae566..6ff3b79 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -418,6 +418,143 @@ __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/{vmid}', ['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'),
+ 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 $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, $vmid, $pool, [ keys %$param]);
+
+ #fixme
+ #&$check_storage_access($rpcenv, $authuser, $storecfg, $vmiddest, $param, $storage);
+
+ #remove snapshots from conf (we only copy from "you are here" for now)
+ delete $conf->{parent};
+ delete $conf->{snapshots};
+
+ 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);
+
+ # 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