use warnings;
use Cwd 'abs_path';
-use PVE::Cluster;
+use PVE::Cluster qw (cfs_read_file cfs_write_file);;
use PVE::SafeSyslog;
use PVE::Tools qw(extract_param);
use PVE::Exception qw(raise raise_param_exc);
}
};
-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;
-};
my $check_storage_access = sub {
- my ($rpcenv, $authuser, $storecfg, $vmid, $pool, $settings, $default_storage) = @_;
+ my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
PVE::QemuServer::foreach_drive($settings, sub {
my ($ds, $drive) = @_;
if (!$volid || $volid eq 'none') {
# nothing to check
+ } elsif ($isCDROM && ($volid eq 'cdrom')) {
+ $rpcenv->check($authuser, "/", ['Sys.Console']);
} elsif (!$isCDROM && ($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' ]);
+ $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
} else {
- my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
- die "image '$path' does not exists\n" if (!(-f $path || -b $path));
+ $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
}
});
};
my $volid = $disk->{file};
- if (!$volid || $volid eq 'none') {
+ if (!$volid || $volid eq 'none' || $volid eq 'cdrom') {
$res->{$ds} = $settings->{$ds};
} elsif ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
my ($storeid, $size) = ($2 || $default_storage, $3);
delete $disk->{format}; # no longer needed
$res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
} else {
- my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
+ my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
+ PVE::Storage::activate_volumes($storecfg, [ $volid ]);
die "image '$path' does not exists\n" if (!(-f $path || -b $path));
$res->{$ds} = $settings->{$ds};
}
my $check_vm_modify_config_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
- return 1 if $authuser ne 'root@pam';
+ return 1 if $authuser eq 'root@pam';
foreach my $opt (@$key_list) {
# disk checks need to be done somewhere else
if ($opt eq 'sockets' || $opt eq 'cores' ||
$opt eq 'cpu' || $opt eq 'smp' ||
- $opt eq 'cpuimit' || $opt eq 'cpuunits') {
+ $opt eq 'cpulimit' || $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']);
$rpcenv->check_perm_modify($authuser, "/pool/$pool");
}
- $rpcenv->check_storage_perm($authuser, $vmid, $pool, $storage, [ 'Datastore.AllocateSpace' ])
+ $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace'])
if defined($storage);
if (!$archive) {
&$resolve_cdrom_alias($param);
- &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $pool, $param, $storage);
+ &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
die "pipe requires cli environment\n"
&& $rpcenv->{type} ne 'cli';
} else {
- my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $archive, $pool);
+ my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
+ PVE::Storage::activate_volumes($storecfg, [ $archive ]);
die "can't find archive file '$archive'\n" if !($path && -f $path);
$archive = $path;
}
}
+ my $addVMtoPoolFn = sub {
+ my $usercfg = cfs_read_file("user.cfg");
+ if (my $data = $usercfg->{pools}->{$pool}) {
+ $data->{vms}->{$vmid} = 1;
+ $usercfg->{vms}->{$vmid} = $pool;
+ cfs_write_file("user.cfg", $usercfg);
+ }
+ };
+
my $restorefn = sub {
if (-f $filename) {
storage => $storage,
pool => $pool,
unique => $unique });
+
+ PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
};
return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd);
my $createfn = sub {
- # second test (after locking test is accurate)
+ # test after locking
die "unable to create vm $vmid: config file already exists\n"
if -f $filename;
$conf->{bootdisk} = $firstdisk;
}
- PVE::QemuServer::update_conf_nolock($vmid, $conf);
+ PVE::QemuServer::update_config_nolock($vmid, $conf);
+
};
my $err = $@;
}
die "create failed - $err";
}
+
+ PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
};
return $rpcenv->fork_worker('qmcreate', $vmid, $authuser, $realcmd);
};
- return PVE::QemuServer::lock_config($vmid, $archive ? $restorefn : $createfn);
+ return PVE::QemuServer::lock_config_full($vmid, 1, $archive ? $restorefn : $createfn);
}});
__PACKAGE__->register_method({
} else {
PVE::QemuServer::add_unused_volume($conf, $volid, $vmid);
}
- delete $conf->{$key};
}
}
+
+ delete $conf->{$key};
};
my $vmconfig_delete_option = sub {
my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
if (my $sid = &$test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) {
- $rpcenv->check_storage_perm($authuser, $vmid, undef, $sid, [ 'Datastore.Allocate' ]);
+ $rpcenv->check($authuser, "/storage/$sid", ['Datastore.Allocate']);
}
}
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
- &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, undef, $param);
+ &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
my $updatefn = sub {
my $storecfg = PVE::Storage::config();
+ my $delVMfromPoolFn = sub {
+ my $usercfg = cfs_read_file("user.cfg");
+ my $pool = $usercfg->{vms}->{$vmid};
+ if (my $data = $usercfg->{pools}->{$pool}) {
+ delete $data->{vms}->{$vmid};
+ delete $usercfg->{vms}->{$vmid};
+ cfs_write_file("user.cfg", $usercfg);
+ }
+ };
+
my $realcmd = sub {
my $upid = shift;
syslog('info', "destroy VM $vmid: $upid\n");
PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock);
+
+ PVE::AccessControl::lock_user_config($delVMfromPoolFn, "pool cleanup failed");
};
return $rpcenv->fork_worker('qmdestroy', $vmid, $authuser, $realcmd);
return $res;
}});
+my $vm_is_ha_managed = sub {
+ my ($vmid) = @_;
+
+ my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
+ if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1)) {
+ return 1;
+ }
+ return 0;
+};
+
__PACKAGE__->register_method({
name => 'vm_status',
path => '{vmid}/status/current',
my $vmstatus = PVE::QemuServer::vmstatus($param->{vmid});
my $status = $vmstatus->{$param->{vmid}};
- my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
- if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $param->{vmid}, 1)) {
- $status->{ha} = 1;
- } else {
- $status->{ha} = 0;
- }
+ $status->{ha} = &$vm_is_ha_managed($param->{vmid});
return $status;
}});
my $storecfg = PVE::Storage::config();
- my $realcmd = sub {
- my $upid = shift;
+ if (&$vm_is_ha_managed($vmid) && !$stateuri &&
+ $rpcenv->{type} ne 'ha') {
- syslog('info', "start VM $vmid: $upid\n");
+ my $hacmd = sub {
+ my $upid = shift;
- PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock);
+ my $service = "pvevm:$vmid";
- return;
- };
+ my $cmd = ['clusvcadm', '-e', $service, '-m', $node];
+
+ print "Executing HA start for VM $vmid\n";
+
+ PVE::Tools::run_command($cmd);
+
+ return;
+ };
+
+ return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
+
+ } else {
+
+ my $realcmd = sub {
+ my $upid = shift;
+
+ syslog('info', "start VM $vmid: $upid\n");
+
+ PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock);
- return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
+ return;
+ };
+
+ return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
+ }
}});
__PACKAGE__->register_method({
my $storecfg = PVE::Storage::config();
- my $realcmd = sub {
- my $upid = shift;
+ if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
- syslog('info', "stop VM $vmid: $upid\n");
+ my $hacmd = sub {
+ my $upid = shift;
- PVE::QemuServer::vm_stop($storecfg, $vmid, $skiplock, 0,
- $param->{timeout}, 0, 1, $keepActive);
+ my $service = "pvevm:$vmid";
- return;
- };
+ my $cmd = ['clusvcadm', '-d', $service];
+
+ print "Executing HA stop for VM $vmid\n";
+
+ PVE::Tools::run_command($cmd);
+
+ return;
+ };
+
+ return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
+
+ } else {
+ my $realcmd = sub {
+ my $upid = shift;
+
+ syslog('info', "stop VM $vmid: $upid\n");
+
+ PVE::QemuServer::vm_stop($storecfg, $vmid, $skiplock, 0,
+ $param->{timeout}, 0, 1, $keepActive);
+
+ return;
+ };
- return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
+ return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
+ }
}});
__PACKAGE__->register_method({
if !$param->{online};
}
- my $realcmd = sub {
- my $upid = shift;
+ if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
- PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
- };
+ my $hacmd = sub {
+ my $upid = shift;
+
+ my $service = "pvevm:$vmid";
+
+ my $cmd = ['clusvcadm', '-M', $service, '-m', $target];
+
+ print "Executing HA migrate for VM $vmid to node $target\n";
+
+ PVE::Tools::run_command($cmd);
+
+ return;
+ };
+
+ return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
- my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
+ } else {
+
+ my $realcmd = sub {
+ my $upid = shift;
+
+ PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
+ };
+
+ return $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
+ }
- return $upid;
}});
__PACKAGE__->register_method({