start adding permission checks

This commit is contained in:
Dietmar Maurer 2012-02-02 06:39:38 +01:00
parent 26ee04b6a0
commit a0d1b1a2fe
4 changed files with 306 additions and 115 deletions

View File

@ -33,11 +33,132 @@ my $resolve_cdrom_alias = sub {
}
};
my $check_volume_access = sub {
my ($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool) = @_;
my $path;
if (my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1)) {
my ($ownervm, $vtype);
($path, $ownervm, $vtype) = PVE::Storage::path($storecfg, $volid);
if ($vtype eq 'iso' || $vtype eq 'vztmpl') {
# we simply allow access
} elsif (!$ownervm || ($ownervm != $vmid)) {
# allow if we are Datastore administrator
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $sid, [ 'Datastore.Allocate' ]);
}
} else {
die "Only root can pass arbitrary filesystem paths."
if $authuser ne 'root@pam';
$path = abs_path($volid);
}
return $path;
};
# 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 {
my ($rpcenv, $authuser, $storecfg, $vmid, $pool, $settings, $conf, $default_storage) = @_;
# check permissions first
my $alloc = [];
foreach_drive($settings, sub {
my ($ds, $disk) = @_;
return if drive_is_cdrom($disk);
my $volid = $disk->{file};
if ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
my ($storeid, $size) = ($2 || $default_storage, $3);
die "no storage ID specified (and no default storage)\n" if !$storeid;
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $storeid, [ 'Datastore.AllocateSpace' ]);
my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
my $fmt = $disk->{format} || $defformat;
push @$alloc, [$ds, $disk, $storeid, $size, $fmt];
} else {
my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
die "image '$path' does not exists\n" if (!(-f $path || -b $path));
}
});
# now try to allocate everything
my $vollist = [];
eval {
foreach my $task (@$alloc) {
my ($ds, $disk, $storeid, $size, $fmt) = @$task;
my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
$fmt, undef, $size*1024*1024);
$disk->{file} = $volid;
push @$vollist, $volid;
}
};
# free allocated images on error
if (my $err = $@) {
syslog('err', "VM $vmid creating disks failed");
foreach my $volid (@$vollist) {
eval { PVE::Storage::vdisk_free($storecfg, $volid); };
warn $@ if $@;
}
die $err;
}
# modify vm config if everything went well
foreach my $task (@$alloc) {
my ($ds, $disk) = @$task;
delete $disk->{format}; # no longer needed
$settings->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
}
return $vollist;
};
my $check_vm_modify_config_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
return 1 if $authuser ne 'root@pam';
foreach my $opt (keys %$param) {
# disk checks need to be done somewhere else
next if PVE::QemuServer::valid_drivename($opt);
if ($opt eq 'sockets' || $opt eq 'cores' ||
$opt eq 'cpu' || $opt eq 'smp' ||
$opt eq 'cpuimit' || $opt eq 'cpuunits') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
} elsif ($opt eq 'boot' || $opt eq 'bootdisk') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
} elsif ($opt eq 'memory' || $opt eq 'balloon') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
} elsif ($opt eq 'args' || $opt eq 'lock') {
die "only root can set '$opt' config\n";
} elsif ($opt eq 'cpu' || $opt eq 'kvm' || $opt eq 'acpi' ||
$opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
} elsif ($opt =~ m/^net\d+$/) {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
} else {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
}
}
return 1;
};
__PACKAGE__->register_method({
name => 'vmlist',
path => '',
method => 'GET',
description => "Virtual machine index (per node).",
permissions => {
description => "Only list VMs where you have VM.Audit permissons on /vms/<vmid>.",
user => 'all',
},
proxyto => 'node',
protected => 1, # qemu pid files are only readable by root
parameters => {
@ -57,10 +178,21 @@ __PACKAGE__->register_method({
code => sub {
my ($param) = @_;
my $rpcenv = PVE::RPCEnvironment::get();
my $authuser = $rpcenv->get_user();
my $vmstatus = PVE::QemuServer::vmstatus();
return PVE::RESTHandler::hash_to_array($vmstatus, 'vmid');
my $res = [];
foreach my $vmid (keys %$vmstatus) {
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
my $data = $vmstatus->{$vmid};
$data->{vmid} = $vmid;
push @$res, $data;
}
return $res;
}});
__PACKAGE__->register_method({
@ -68,6 +200,13 @@ __PACKAGE__->register_method({
path => '',
method => 'POST',
description => "Create or restore 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 => {
@ -98,6 +237,11 @@ __PACKAGE__->register_method({
description => "Assign a unique random ethernet address.",
requires => 'archive',
},
pool => {
optional => 1,
type => 'string', format => 'pve-poolid',
description => "Add the VM to the specified pool.",
},
}),
},
returns => {
@ -108,7 +252,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -121,6 +265,8 @@ __PACKAGE__->register_method({
my $force = extract_param($param, 'force');
my $unique = extract_param($param, 'unique');
my $pool = extract_param($param, 'pool');
my $filename = PVE::QemuServer::config_file($vmid);
@ -128,6 +274,14 @@ __PACKAGE__->register_method({
PVE::Cluster::check_cfs_quorum();
if (defined($pool)) {
$rpcenv->check_pool_exist($pool);
$rpcenv->check_perm_modify($authuser, "/pool/$pool");
}
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $storage, [ 'Datastore.AllocateSpace' ])
if defined($storage);
if (!$archive) {
&$resolve_cdrom_alias($param);
@ -150,15 +304,7 @@ __PACKAGE__->register_method({
die "pipe requires cli environment\n"
&& $rpcenv->{type} ne 'cli';
} else {
my $path;
if (PVE::Storage::parse_volume_id($archive, 1)) {
$path = PVE::Storage::path($storecfg, $archive);
} else {
raise_param_exc({ archive => "Only root can pass arbitrary paths." })
if $user ne 'root@pam';
$path = abs_path($archive);
}
my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $archive, $pool);
die "can't find archive file '$archive'\n" if !($path && -f $path);
$archive = $path;
}
@ -178,14 +324,17 @@ __PACKAGE__->register_method({
}
my $realcmd = sub {
PVE::QemuServer::restore_archive($archive, $vmid, {
PVE::QemuServer::restore_archive($archive, $vmid, $authuser, {
storage => $storage,
pool => $pool,
unique => $unique });
};
return $rpcenv->fork_worker('qmrestore', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd);
};
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, $param);
my $createfn = sub {
# second test (after locking test is accurate)
@ -197,7 +346,7 @@ __PACKAGE__->register_method({
my $vollist = [];
eval {
$vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param, $storage);
$vollist = &$create_disks($rpcenv, $authuser, $storecfg, $vmid, $pool, $param, $storage);
# try to be smart about bootdisk
my @disks = PVE::QemuServer::disknames();
@ -226,7 +375,7 @@ __PACKAGE__->register_method({
}
};
return $rpcenv->fork_worker('qmcreate', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmcreate', $vmid, $authuser, $realcmd);
};
return PVE::QemuServer::lock_config($vmid, $archive ? $restorefn : $createfn);
@ -238,6 +387,9 @@ __PACKAGE__->register_method({
method => 'GET',
proxyto => 'node',
description => "Directory index",
permissions => {
user => 'all',
},
parameters => {
additionalProperties => 0,
properties => {
@ -366,6 +518,9 @@ __PACKAGE__->register_method({
method => 'GET',
proxyto => 'node',
description => "Get virtual machine configuration.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -390,6 +545,16 @@ __PACKAGE__->register_method({
return $conf;
}});
my $vm_config_perm_list = [
'VM.Config.Disk',
'VM.Config.CDROM',
'VM.Config.CPU',
'VM.Config.Memory',
'VM.Config.Network',
'VM.Config.HWType',
'VM.Config.Options',
];
__PACKAGE__->register_method({
name => 'update_vm',
path => '{vmid}/config',
@ -397,6 +562,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Set virtual machine options.",
permissions => {
check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
},
parameters => {
additionalProperties => 0,
properties => PVE::QemuServer::json_config_properties(
@ -429,7 +597,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -444,7 +612,7 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
my $delete = extract_param($param, 'delete');
my $force = extract_param($param, 'force');
@ -464,7 +632,7 @@ __PACKAGE__->register_method({
PVE::QemuServer::check_lock($conf) if !$skiplock;
PVE::Cluster::log_msg('info', $user, "update VM $vmid: " . join (' ', @paramarr));
PVE::Cluster::log_msg('info', $authuser, "update VM $vmid: " . join (' ', @paramarr));
#delete
foreach my $opt (PVE::Tools::split_list($delete)) {
@ -552,7 +720,7 @@ __PACKAGE__->register_method({
}
}
my $settings = { $opt => $param->{$opt} };
PVE::QemuServer::create_disks($storecfg, $vmid, $settings, $conf);
&$create_disks($rpcenv, $authuser, $storecfg, $vmid, undef, $settings, $conf);
$param->{$opt} = $settings->{$opt};
#hotplug disks
if(!PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive)) {
@ -597,6 +765,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Destroy the vm (also delete all used/owned volumes).",
permissions => {
check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
@ -613,13 +784,13 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $vmid = $param->{vmid};
my $skiplock = $param->{skiplock};
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
# test if VM exists
my $conf = PVE::QemuServer::load_config($vmid);
@ -634,7 +805,7 @@ __PACKAGE__->register_method({
PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock);
};
return $rpcenv->fork_worker('qmdestroy', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmdestroy', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -644,6 +815,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Unlink/delete disk images.",
permissions => {
check => [ 'perm', '/vms/{vmid}', ['VM.Config.Disk']],
},
parameters => {
additionalProperties => 0,
properties => {
@ -704,14 +878,14 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $vmid = $param->{vmid};
my $node = $param->{node};
my $authpath = "/vms/$vmid";
my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
$sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
if !$sslcert;
@ -748,10 +922,10 @@ __PACKAGE__->register_method({
return;
};
my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $user, $realcmd);
my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
return {
user => $user,
user => $authuser,
ticket => $ticket,
port => $port,
upid => $upid,
@ -765,6 +939,9 @@ __PACKAGE__->register_method({
method => 'GET',
proxyto => 'node',
description => "Directory index",
permissions => {
user => 'all',
},
parameters => {
additionalProperties => 0,
properties => {
@ -804,6 +981,9 @@ __PACKAGE__->register_method({
proxyto => 'node',
protected => 1, # qemu pid files are only readable by root
description => "Get virtual machine status.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -838,6 +1018,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Start virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -855,7 +1038,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -863,11 +1046,11 @@ __PACKAGE__->register_method({
my $stateuri = extract_param($param, 'stateuri');
raise_param_exc({ stateuri => "Only root may use this option." })
if $stateuri && $user ne 'root@pam';
if $stateuri && $authuser ne 'root@pam';
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
my $storecfg = PVE::Storage::config();
@ -881,7 +1064,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmstart', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -891,6 +1074,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Stop virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -919,7 +1105,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -927,11 +1113,11 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
my $keepActive = extract_param($param, 'keepActive');
raise_param_exc({ keepActive => "Only root may use this option." })
if $keepActive && $user ne 'root@pam';
if $keepActive && $authuser ne 'root@pam';
my $storecfg = PVE::Storage::config();
@ -946,7 +1132,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmstop', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -956,6 +1142,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Reset virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -972,7 +1161,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -980,7 +1169,7 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
@ -992,7 +1181,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmreset', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmreset', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -1002,6 +1191,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Shutdown virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -1036,7 +1228,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -1044,11 +1236,11 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
my $keepActive = extract_param($param, 'keepActive');
raise_param_exc({ keepActive => "Only root may use this option." })
if $keepActive && $user ne 'root@pam';
if $keepActive && $authuser ne 'root@pam';
my $storecfg = PVE::Storage::config();
@ -1063,7 +1255,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmshutdown', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmshutdown', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -1073,6 +1265,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Suspend virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -1089,7 +1284,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -1097,7 +1292,7 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
@ -1111,7 +1306,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmsuspend', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmsuspend', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -1121,6 +1316,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Resume virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -1137,7 +1335,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -1145,7 +1343,7 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
@ -1159,7 +1357,7 @@ __PACKAGE__->register_method({
return;
};
return $rpcenv->fork_worker('qmresume', $vmid, $user, $realcmd);
return $rpcenv->fork_worker('qmresume', $vmid, $authuser, $realcmd);
}});
__PACKAGE__->register_method({
@ -1169,6 +1367,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Send key event to virtual machine.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -1187,7 +1388,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $node = extract_param($param, 'node');
@ -1195,7 +1396,7 @@ __PACKAGE__->register_method({
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $user ne 'root@pam';
if $skiplock && $authuser ne 'root@pam';
PVE::QemuServer::vm_sendkey($vmid, $skiplock, $param->{key});
@ -1209,6 +1410,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Migrate virtual machine. Creates a new migration task.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
},
parameters => {
additionalProperties => 0,
properties => {
@ -1236,7 +1440,7 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $authuser = $rpcenv->get_user();
my $target = extract_param($param, 'target');
@ -1252,7 +1456,7 @@ __PACKAGE__->register_method({
my $vmid = extract_param($param, 'vmid');
raise_param_exc({ force => "Only root may use this option." })
if $param->{force} && $user ne 'root@pam';
if $param->{force} && $authuser ne 'root@pam';
# test if VM exists
my $conf = PVE::QemuServer::load_config($vmid);
@ -1272,7 +1476,7 @@ __PACKAGE__->register_method({
PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
};
my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $user, $realcmd);
my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
return $upid;
}});
@ -1284,6 +1488,9 @@ __PACKAGE__->register_method({
protected => 1,
proxyto => 'node',
description => "Execute Qemu monitor commands.",
permissions => {
check => ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
},
parameters => {
additionalProperties => 0,
properties => {

View File

@ -1318,61 +1318,6 @@ sub touch_config {
utime undef, undef, $conf;
}
sub create_disks {
my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
my $vollist = [];
eval {
foreach_drive($settings, sub {
my ($ds, $disk) = @_;
return if drive_is_cdrom($disk);
my $file = $disk->{file};
if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
my $storeid = $2 || $default_storage;
my $size = $3;
my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
my $fmt = $disk->{format} || $defformat;
syslog('info', "VM $vmid creating new disk - size is $size GB");
my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
$fmt, undef, $size*1024*1024);
$disk->{file} = $volid;
delete $disk->{format}; # no longer needed
push @$vollist, $volid;
$settings->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
} else {
my $path;
if ($disk->{file} =~ m|^/dev/.+|) {
$path = $disk->{file};
} else {
$path = PVE::Storage::path($storecfg, $disk->{file});
}
if (!(-f $path || -b $path)) {
die "image '$path' does not exists\n";
}
}
});
};
my $err = $@;
if ($err) {
syslog('err', "VM $vmid creating disks failed");
foreach my $volid (@$vollist) {
eval { PVE::Storage::vdisk_free($storecfg, $volid); };
warn $@ if $@;
}
die $err;
}
return $vollist;
}
sub destroy_vm {
my ($storecfg, $vmid, $keep_empty_config) = @_;
@ -3157,7 +3102,7 @@ sub restore_cleanup {
}
sub restore_archive {
my ($archive, $vmid, $opts) = @_;
my ($archive, $vmid, $user, $opts) = @_;
if ($archive ne '-') {
my $firstfile = archive_read_firstfile($archive);
@ -3168,10 +3113,11 @@ sub restore_archive {
my $tocmd = "/usr/lib/qemu-server/qmextract";
$tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
$tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
$tocmd .= ' --prealloc' if $opts->{prealloc};
$tocmd .= ' --info' if $opts->{info};
# tar option "xf" does not autodetect compression when read fron STDIN,
# tar option "xf" does not autodetect compression when read from STDIN,
# so we pipe to zcat
my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
PVE::Tools::shellquote("--to-command=$tocmd");
@ -3181,6 +3127,7 @@ sub restore_archive {
local $ENV{VZDUMP_TMPDIR} = $tmpdir;
local $ENV{VZDUMP_VMID} = $vmid;
local $ENV{VZDUMP_USER} = $user;
my $conffile = PVE::QemuServer::config_file($vmid);
my $tmpfn = "$conffile.$$.tmp";

View File

@ -4,15 +4,21 @@ use strict;
use Getopt::Long;
use File::Path;
use IO::File;
use PVE::INotify;
use PVE::JSONSchema;
use PVE::Tools;
use PVE::Cluster qw(cfs_read_file);
use PVE::RPCEnvironment;
use PVE::QemuServer;
my @std_opts = ('storage=s', 'info', 'prealloc');
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
die "please run as root\n" if $> != 0;
my @std_opts = ('storage=s', 'pool=s', 'info', 'prealloc');
sub print_usage {
print STDERR "usage: $0 [--storage=<storeid>] [--info] [--prealloc] <archive> <vmid>\n\n";
print STDERR "usage: $0 [--storage=<storeid>] [--pool=<poolid>] [--info] [--prealloc] <archive> <vmid>\n\n";
}
my $opts = {};
@ -21,6 +27,14 @@ if (!GetOptions ($opts, @std_opts)) {
exit (-1);
}
PVE::INotify::inotify_init();
my $rpcenv = PVE::RPCEnvironment->init('cli');
$rpcenv->init_request();
$rpcenv->set_language($ENV{LANG});
$rpcenv->set_user('root@pam');
sub extract_archive {
# NOTE: this is run as tar subprocess (--to-command)
@ -43,6 +57,9 @@ sub extract_archive {
my $vmid = $ENV{VZDUMP_VMID};
PVE::JSONSchema::pve_verify_vmid($vmid);
my $user = $ENV{VZDUMP_USER};
$rpcenv->check_user_enabled($user);
if ($opts->{info}) {
print STDERR "reading archive member '$filename'\n";
} else {
@ -96,7 +113,7 @@ sub extract_archive {
print STDERR "restoring old style vzdump archive - " .
"no device map inside archive\n";
die "can't restore old style archive to storage '$opts->{storage}'\n"
if $opts->{storage} && $opts->{storage} ne 'local';
if defined($opts->{storage}) && $opts->{storage} ne 'local';
my $dir = "/var/lib/vz/images/$vmid";
mkpath $dir;
@ -120,9 +137,24 @@ sub extract_archive {
"($filesize != $info->{size})\n";
}
# check permission for all used storages
my $pool = $opts->{pool};
if ($user ne 'root@pam') {
if (defined($opts->{storage})) {
my $sid = $opts->{storage} || 'local';
$rpcenv->check_storage_perm($user, $vmid, $pool, $sid, ['Datastore.AllocateSpace']);
} else {
foreach my $fn (keys %$map) {
my $fi = $map->{$fn};
my $sid = $fi->{storeid} || 'local';
$rpcenv->check_storage_perm($user, $vmid, $pool, $sid, ['Datastore.AllocateSpace']);
}
}
}
my $storeid;
if ($opts->{storage}) {
$storeid = $opts->{storage};
if (defined($opts->{storage})) {
$storeid = $opts->{storage} || 'local';
} else {
$storeid = $info->{storeid} || 'local';
}
@ -172,7 +204,7 @@ if (scalar(@ARGV) == 2) {
PVE::Cluster::check_cfs_quorum();
PVE::QemuServer::restore_archive($archive, $vmid, $opts);
PVE::QemuServer::restore_archive($archive, $vmid, 'root@pam', $opts);
} elsif (scalar(@ARGV) == 0 && $ENV{TAR_FILENAME}) {
extract_archive();

View File

@ -55,6 +55,11 @@ __PACKAGE__->register_method({
type => 'boolean',
description => "Assign a unique random ethernet address.",
},
pool => {
optional => 1,
type => 'string', format => 'pve-poolid',
description => "Add the VM to the specified pool.",
},
},
},
returns => {