mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-08-03 13:04:12 +00:00
implement shared file locks
and add a first prototype for copy_vm
This commit is contained in:
parent
6fa741fcc3
commit
6116f72902
2
Makefile
2
Makefile
@ -2,7 +2,7 @@ RELEASE=3.0
|
||||
|
||||
VERSION=3.0
|
||||
PACKAGE=qemu-server
|
||||
PKGREL=4
|
||||
PKGREL=5
|
||||
|
||||
DESTDIR=
|
||||
PREFIX=/usr
|
||||
|
189
PVE/API2/Qemu.pm
189
PVE/API2/Qemu.pm
@ -59,6 +59,32 @@ my $check_storage_access = sub {
|
||||
});
|
||||
};
|
||||
|
||||
my $check_storage_access_copy = sub {
|
||||
my ($rpcenv, $authuser, $storecfg, $conf) = @_;
|
||||
|
||||
PVE::QemuServer::foreach_drive($conf, sub {
|
||||
my ($ds, $drive) = @_;
|
||||
|
||||
my $isCDROM = PVE::QemuServer::drive_is_cdrom($drive);
|
||||
|
||||
my $volid = $drive->{file};
|
||||
|
||||
return if !$volid || $volid eq 'none';
|
||||
|
||||
if ($isCDROM) {
|
||||
if ($volid eq 'cdrom') {
|
||||
$rpcenv->check($authuser, "/", ['Sys.Console']);
|
||||
} else {
|
||||
# we simply allow access
|
||||
}
|
||||
} else {
|
||||
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
|
||||
die "unable to copy arbitrary files\n" if !$sid;
|
||||
$rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
# Note: $pool is only needed when creating a VM, because pool permissions
|
||||
# are automatically inherited if VM already exists inside a pool.
|
||||
my $create_disks = sub {
|
||||
@ -344,6 +370,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $restorefn = sub {
|
||||
|
||||
# fixme: this test does not work if VM exists on other node!
|
||||
if (-f $filename) {
|
||||
die "unable to restore vm $vmid: config file already exists\n"
|
||||
if !$force;
|
||||
@ -1746,6 +1773,168 @@ __PACKAGE__->register_method({
|
||||
return $res;
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'copy_vm',
|
||||
path => '{vmid}/copy',
|
||||
method => 'POST',
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Creat a copy of virtual machine/template.",
|
||||
permissions => {
|
||||
description => "You need 'VM.Copy' permissions on /vms/{vmid}, and 'VM.Allocate' permissions " .
|
||||
"on /vms/{newid} (or on the VM pool /pool/{pool}). You also need " .
|
||||
"'Datastore.AllocateSpace' on any used storage.",
|
||||
check =>
|
||||
[ 'and',
|
||||
['perm', '/vms/{vmid}', [ 'VM.Copy' ]],
|
||||
[ 'or',
|
||||
[ 'perm', '/vms/{newid}', ['VM.Allocate']],
|
||||
[ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param => 'pool'],
|
||||
],
|
||||
]
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
# fixme: add other parameters like name and description?
|
||||
node => get_standard_option('pve-node'),
|
||||
vmid => get_standard_option('pve-vmid'),
|
||||
newid => get_standard_option('pve-vmid', {
|
||||
description => 'VMID for the copy.' }),
|
||||
pool => {
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-poolid',
|
||||
description => "Add the new VM to the specified pool.",
|
||||
},
|
||||
full => {
|
||||
optional => 1,
|
||||
type => 'boolean',
|
||||
description => "Create a full copy of all disk. This is always done when " .
|
||||
"you copy a normal VM. For VM templates, we try to create a linked copy by default.",
|
||||
default => 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
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 $newid = extract_param($param, 'newid');
|
||||
|
||||
# fixme: update pool after create
|
||||
my $pool = extract_param($param, 'pool');
|
||||
|
||||
if (defined($pool)) {
|
||||
$rpcenv->check_pool_exist($pool);
|
||||
}
|
||||
|
||||
my $storecfg = PVE::Storage::config();
|
||||
|
||||
PVE::Cluster::check_cfs_quorum();
|
||||
|
||||
# fixme: do early checks - re-check after lock
|
||||
|
||||
# fixme: impl. target node parameter (mv VM config if all storages are shared)
|
||||
|
||||
my $copyfn = sub {
|
||||
|
||||
# all tests after lock
|
||||
my $conf = PVE::QemuServer::load_config($vmid);
|
||||
|
||||
PVE::QemuServer::check_lock($conf);
|
||||
|
||||
my $running = PVE::QemuServer::check_running($vmid);
|
||||
|
||||
die "Copy running VM $vmid not implemented\n" if $running;
|
||||
|
||||
&$check_storage_access_copy($rpcenv, $authuser, $storecfg, $conf);
|
||||
|
||||
# fixme: snapshots??
|
||||
|
||||
my $conffile = PVE::QemuServer::config_file($newid);
|
||||
|
||||
die "unable to create VM $newid: config file already exists\n"
|
||||
if -f $conffile;
|
||||
|
||||
# create empty/temp config - this fails if VM already exists on other node
|
||||
PVE::Tools::file_set_contents($conffile, "# qmcopy temporary file\n");
|
||||
|
||||
my $realcmd = sub {
|
||||
my $upid = shift;
|
||||
|
||||
eval {
|
||||
print "COPY VM $vmid start\n";
|
||||
|
||||
my $newconf = {};
|
||||
my $drives = {};
|
||||
my $vollist = [];
|
||||
foreach my $opt (keys %$conf) {
|
||||
my $value = $conf->{$opt};
|
||||
|
||||
# always change MAC! address
|
||||
if ($opt =~ m/^net(\d+)$/) {
|
||||
my $net = PVE::QemuServer::parse_net($value);
|
||||
$net->{macaddr} = PVE::Tools::random_ether_addr();
|
||||
$newconf->{$opt} = PVE::QemuServer::print_net($net);
|
||||
} elsif (my $drive = PVE::QemuServer::parse_drive($opt, $value)) {
|
||||
if (PVE::QemuServer::drive_is_cdrom($drive)) {
|
||||
$newconf->{$opt} = $value; # simply copy configuration
|
||||
} else {
|
||||
$drives->{$opt} = $drive;
|
||||
push @$vollist, $drive->{file};
|
||||
}
|
||||
} else {
|
||||
# copy everything else
|
||||
$newconf->{$opt} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
delete $newconf->{template};
|
||||
|
||||
PVE::Storage::activate_volumes($storecfg, $vollist);
|
||||
|
||||
foreach my $opt (keys %$drives) {
|
||||
my $drive = $drives->{$opt};
|
||||
print "COPY drive $opt: $drive->{file}\n";
|
||||
|
||||
}
|
||||
|
||||
sleep(10);
|
||||
PVE::QemuServer::update_config_nolock($newid, $newconf, 1);
|
||||
|
||||
print "COPY VM $vmid end\n";
|
||||
};
|
||||
if (my $err = $@) {
|
||||
# fixme: remove all created files
|
||||
unlink $conffile;
|
||||
|
||||
die "copy failed: $err";
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmcopy', $vmid, $authuser, $realcmd);
|
||||
};
|
||||
|
||||
# Aquire shared lock for $vmid
|
||||
return PVE::QemuServer::lock_config_shared($vmid, 1, sub {
|
||||
# Aquire exclusive lock lock for $newid
|
||||
return PVE::QemuServer::lock_config_full($newid, 1, $copyfn);
|
||||
});
|
||||
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'migrate_vm',
|
||||
path => '{vmid}/migrate',
|
||||
|
@ -21,7 +21,7 @@ use PVE::SafeSyslog;
|
||||
use Storable qw(dclone);
|
||||
use PVE::Exception qw(raise raise_param_exc);
|
||||
use PVE::Storage;
|
||||
use PVE::Tools qw(run_command lock_file file_read_firstline);
|
||||
use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline);
|
||||
use PVE::JSONSchema qw(get_standard_option);
|
||||
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
|
||||
use PVE::INotify;
|
||||
@ -1479,6 +1479,18 @@ sub lock_config_full {
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub lock_config_shared {
|
||||
my ($vmid, $timeout, $code, @param) = @_;
|
||||
|
||||
my $filename = config_file_lock($vmid);
|
||||
|
||||
my $res = lock_file_full($filename, $timeout, 1, $code, @param);
|
||||
|
||||
die $@ if $@;
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub lock_config {
|
||||
my ($vmid, $code, @param) = @_;
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
qemu-server (3.0-5) unstable; urgency=low
|
||||
|
||||
* implement shared file locks
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Thu, 25 Apr 2013 11:35:01 +0200
|
||||
|
||||
qemu-server (3.0-4) unstable; urgency=low
|
||||
|
||||
* fix bug 377: make qm rescan work properly
|
||||
|
2
qm
2
qm
@ -351,6 +351,8 @@ my $cmddef = {
|
||||
|
||||
destroy => [ "PVE::API2::Qemu", 'destroy_vm', ['vmid'], { node => $nodename }, $upid_exit ],
|
||||
|
||||
copy => [ "PVE::API2::Qemu", 'copy_vm', ['vmid', 'newid'], { node => $nodename }, $upid_exit ],
|
||||
|
||||
migrate => [ "PVE::API2::Qemu", 'migrate_vm', ['vmid', 'target'], { node => $nodename }, $upid_exit ],
|
||||
|
||||
set => [ "PVE::API2::Qemu", 'update_vm', ['vmid'], { node => $nodename } ],
|
||||
|
Loading…
Reference in New Issue
Block a user