mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-05-30 00:30:42 +00:00
copy_vm: new option to move final VM to other node (option target)
This only works if the VM is on shared storage.
This commit is contained in:
parent
42a19c87bc
commit
55173c6bd4
@ -62,6 +62,8 @@ my $check_storage_access = sub {
|
|||||||
my $check_storage_access_copy = sub {
|
my $check_storage_access_copy = sub {
|
||||||
my ($rpcenv, $authuser, $storecfg, $conf, $storage) = @_;
|
my ($rpcenv, $authuser, $storecfg, $conf, $storage) = @_;
|
||||||
|
|
||||||
|
my $sharedvm = 1;
|
||||||
|
|
||||||
PVE::QemuServer::foreach_drive($conf, sub {
|
PVE::QemuServer::foreach_drive($conf, sub {
|
||||||
my ($ds, $drive) = @_;
|
my ($ds, $drive) = @_;
|
||||||
|
|
||||||
@ -76,14 +78,22 @@ my $check_storage_access_copy = sub {
|
|||||||
$rpcenv->check($authuser, "/", ['Sys.Console']);
|
$rpcenv->check($authuser, "/", ['Sys.Console']);
|
||||||
} else {
|
} else {
|
||||||
# we simply allow access
|
# we simply allow access
|
||||||
|
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
|
||||||
|
my $scfg = PVE::Storage::storage_config($storecfg, $sid);
|
||||||
|
$sharedvm = 0 if !$scfg->{shared};
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
|
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
|
||||||
die "unable to copy arbitrary files\n" if !$sid;
|
my $scfg = PVE::Storage::storage_config($storecfg, $sid);
|
||||||
|
$sharedvm = 0 if !$scfg->{shared};
|
||||||
|
|
||||||
$sid = $storage if $storage;
|
$sid = $storage if $storage;
|
||||||
$rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
|
$rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return $sharedvm;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Note: $pool is only needed when creating a VM, because pool permissions
|
# Note: $pool is only needed when creating a VM, because pool permissions
|
||||||
@ -1823,21 +1833,25 @@ __PACKAGE__->register_method({
|
|||||||
requires => 'full',
|
requires => 'full',
|
||||||
optional => 1,
|
optional => 1,
|
||||||
}),
|
}),
|
||||||
format => {
|
'format' => {
|
||||||
description => "Target format for file storage.",
|
description => "Target format for file storage.",
|
||||||
requires => 'full',
|
requires => 'full',
|
||||||
type => 'string',
|
type => 'string',
|
||||||
optional => 1,
|
optional => 1,
|
||||||
enum => [ 'raw', 'qcow2', 'vmdk'],
|
enum => [ 'raw', 'qcow2', 'vmdk'],
|
||||||
},
|
},
|
||||||
full => {
|
full => {
|
||||||
optional => 1,
|
optional => 1,
|
||||||
type => 'boolean',
|
type => 'boolean',
|
||||||
description => "Create a full copy of all disk. This is always done when " .
|
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.",
|
"you copy a normal VM. For VM templates, we try to create a linked copy by default.",
|
||||||
default => 0,
|
default => 0,
|
||||||
},
|
},
|
||||||
},
|
target => get_standard_option('pve-node', {
|
||||||
|
description => "Target node. Only allowed if the original VM is on shared storage.",
|
||||||
|
optional => 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
returns => {
|
returns => {
|
||||||
type => 'string',
|
type => 'string',
|
||||||
@ -1847,7 +1861,7 @@ __PACKAGE__->register_method({
|
|||||||
|
|
||||||
my $rpcenv = PVE::RPCEnvironment::get();
|
my $rpcenv = PVE::RPCEnvironment::get();
|
||||||
|
|
||||||
my $authuser = $rpcenv->get_user();
|
my $authuser = $rpcenv->get_user();
|
||||||
|
|
||||||
my $node = extract_param($param, 'node');
|
my $node = extract_param($param, 'node');
|
||||||
|
|
||||||
@ -1862,15 +1876,23 @@ __PACKAGE__->register_method({
|
|||||||
$rpcenv->check_pool_exist($pool);
|
$rpcenv->check_pool_exist($pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $snapname = extract_param($param, 'snapname');
|
my $snapname = extract_param($param, 'snapname');
|
||||||
|
|
||||||
my $storage = extract_param($param, 'storage');
|
my $storage = extract_param($param, 'storage');
|
||||||
|
|
||||||
my $format = extract_param($param, 'format');
|
my $format = extract_param($param, 'format');
|
||||||
|
|
||||||
|
my $target = extract_param($param, 'target');
|
||||||
|
|
||||||
|
my $localnode = PVE::INotify::nodename();
|
||||||
|
|
||||||
|
undef $target if $target eq $localnode || $target eq 'localhost';
|
||||||
|
|
||||||
|
PVE::Cluster::check_node_exists($target) if $target;
|
||||||
|
|
||||||
my $storecfg = PVE::Storage::config();
|
my $storecfg = PVE::Storage::config();
|
||||||
|
|
||||||
PVE::Cluster::check_cfs_quorum();
|
PVE::Cluster::check_cfs_quorum();
|
||||||
|
|
||||||
my $running = PVE::QemuServer::check_running($vmid) || 0;
|
my $running = PVE::QemuServer::check_running($vmid) || 0;
|
||||||
|
|
||||||
@ -1899,8 +1921,10 @@ __PACKAGE__->register_method({
|
|||||||
|
|
||||||
my $oldconf = $snapname ? $conf->{snapshots}->{$snapname} : $conf;
|
my $oldconf = $snapname ? $conf->{snapshots}->{$snapname} : $conf;
|
||||||
|
|
||||||
&$check_storage_access_copy($rpcenv, $authuser, $storecfg, $oldconf, $storage);
|
my $sharedvm = &$check_storage_access_copy($rpcenv, $authuser, $storecfg, $oldconf, $storage);
|
||||||
|
|
||||||
|
die "can't copy VM to node '$target' (VM uses local storage)\n" if $target && !$sharedvm;
|
||||||
|
|
||||||
my $conffile = PVE::QemuServer::config_file($newid);
|
my $conffile = PVE::QemuServer::config_file($newid);
|
||||||
|
|
||||||
die "unable to create VM $newid: config file already exists\n"
|
die "unable to create VM $newid: config file already exists\n"
|
||||||
@ -2000,6 +2024,12 @@ __PACKAGE__->register_method({
|
|||||||
|
|
||||||
delete $newconf->{lock};
|
delete $newconf->{lock};
|
||||||
PVE::QemuServer::update_config_nolock($newid, $newconf, 1);
|
PVE::QemuServer::update_config_nolock($newid, $newconf, 1);
|
||||||
|
|
||||||
|
if ($target) {
|
||||||
|
my $newconffile = PVE::QemuServer::config_file($newid, $target);
|
||||||
|
die "Failed to move config to node '$target' - rename failed: $!\n"
|
||||||
|
if !rename($conffile, $newconffile);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (my $err = $@) {
|
if (my $err = $@) {
|
||||||
unlink $conffile;
|
unlink $conffile;
|
||||||
|
Loading…
Reference in New Issue
Block a user