X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FAPI2%2FQemu.pm;h=9cfaa29fdaa0ee393227ffa45d839c2916e47fd7;hb=d3efae29f5ecc4bcd9452fd434ae7c70f59b8fd9;hp=b22093427aa2a55e948f685bcfffabf853e72174;hpb=ea1c21108f774c3440704780bda335535f9a95a4;p=qemu-server.git diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index b220934..9cfaa29 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -21,6 +21,7 @@ use PVE::GuestHelpers; use PVE::QemuConfig; use PVE::QemuServer; use PVE::QemuServer::Drive; +use PVE::QemuServer::CPUConfig; use PVE::QemuServer::Monitor qw(mon_cmd); use PVE::QemuMigrate; use PVE::RPCEnvironment; @@ -63,7 +64,7 @@ my $NEW_DISK_RE = qr!^(([^/:\s]+):)?(\d+(\.\d+)?)$!; my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; - PVE::QemuServer::foreach_drive($settings, sub { + PVE::QemuConfig->foreach_volume($settings, sub { my ($ds, $drive) = @_; my $isCDROM = PVE::QemuServer::drive_is_cdrom($drive); @@ -96,7 +97,7 @@ my $check_storage_access_clone = sub { my $sharedvm = 1; - PVE::QemuServer::foreach_drive($conf, sub { + PVE::QemuConfig->foreach_volume($conf, sub { my ($ds, $drive) = @_; my $isCDROM = PVE::QemuServer::drive_is_cdrom($drive); @@ -216,7 +217,7 @@ my $create_disks = sub { } }; - eval { PVE::QemuServer::foreach_drive($settings, $code); }; + eval { PVE::QemuConfig->foreach_volume($settings, $code); }; # free allocated images on error if (my $err = $@) { @@ -236,6 +237,26 @@ my $create_disks = sub { return $vollist; }; +my $check_cpu_model_access = sub { + my ($rpcenv, $authuser, $new, $existing) = @_; + + return if !defined($new->{cpu}); + + my $cpu = PVE::JSONSchema::check_format('pve-vm-cpu-conf', $new->{cpu}); + return if !$cpu || !$cpu->{cputype}; # always allow default + my $cputype = $cpu->{cputype}; + + if ($existing && $existing->{cpu}) { + # changing only other settings doesn't require permissions for CPU model + my $existingCpu = PVE::JSONSchema::check_format('pve-vm-cpu-conf', $existing->{cpu}); + return if $existingCpu->{cputype} eq $cputype; + } + + if (PVE::QemuServer::CPUConfig::is_custom_model($cputype)) { + $rpcenv->check($authuser, "/nodes", ['Sys.Audit']); + } +}; + my $cpuoptions = { 'cores' => 1, 'cpu' => 1, @@ -543,6 +564,8 @@ __PACKAGE__->register_method({ &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]); + &$check_cpu_model_access($rpcenv, $authuser, $param); + foreach my $opt (keys %$param) { if (PVE::QemuServer::is_valid_drivename($opt)) { my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt}); @@ -1072,7 +1095,10 @@ my $update_vm_api = sub { return if PVE::QemuServer::drive_is_cdrom($drive); my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); - return if $volname eq 'cloudinit'; + die "cannot add non-managed/pass-through volume to a replicated VM\n" + if !defined($storeid); + + return if defined($volname) && $volname eq 'cloudinit'; my $format; if ($volid =~ $NEW_DISK_RE) { @@ -1122,6 +1148,8 @@ my $update_vm_api = sub { die "checksum missmatch (file change by other user?)\n" if $digest && $digest ne $conf->{digest}; + &$check_cpu_model_access($rpcenv, $authuser, $param, $conf); + # FIXME: 'suspended' lock should probabyl be a state or "weak" lock?! if (scalar(@delete) && grep { $_ eq 'vmstate'} @delete) { if (defined($conf->{lock}) && $conf->{lock} eq 'suspended') { @@ -1468,32 +1496,38 @@ __PACKAGE__->register_method({ raise_param_exc({ skiplock => "Only root may use this option." }) if $skiplock && $authuser ne 'root@pam'; - # test if VM exists - my $conf = PVE::QemuConfig->load_config($vmid); - my $storecfg = PVE::Storage::config(); - PVE::QemuConfig->check_protection($conf, "can't remove VM $vmid"); + my $early_checks = sub { + # test if VM exists + my $conf = PVE::QemuConfig->load_config($vmid); + PVE::QemuConfig->check_protection($conf, "can't remove VM $vmid"); - my $ha_managed = PVE::HA::Config::service_is_configured("vm:$vmid"); + my $ha_managed = PVE::HA::Config::service_is_configured("vm:$vmid"); - if (!$param->{purge}) { - die "unable to remove VM $vmid - used in HA resources and purge parameter not set.\n" - if $ha_managed; - # don't allow destroy if with replication jobs but no purge param - my $repl_conf = PVE::ReplicationConfig->new(); - $repl_conf->check_for_existing_jobs($vmid); - } + if (!$param->{purge}) { + die "unable to remove VM $vmid - used in HA resources and purge parameter not set.\n" + if $ha_managed; + # don't allow destroy if with replication jobs but no purge param + my $repl_conf = PVE::ReplicationConfig->new(); + $repl_conf->check_for_existing_jobs($vmid); + } + + die "VM $vmid is running - destroy failed\n" + if PVE::QemuServer::check_running($vmid); - # early tests (repeat after locking) - die "VM $vmid is running - destroy failed\n" - if PVE::QemuServer::check_running($vmid); + return $ha_managed; + }; + + $early_checks->(); my $realcmd = sub { my $upid = shift; + my $storecfg = PVE::Storage::config(); + syslog('info', "destroy VM $vmid: $upid\n"); PVE::QemuConfig->lock_config($vmid, sub { - die "VM $vmid is running - destroy failed\n" - if (PVE::QemuServer::check_running($vmid)); + # repeat, config might have changed + my $ha_managed = $early_checks->(); PVE::QemuServer::destroy_vm($storecfg, $vmid, $skiplock, { lock => 'destroyed' }); @@ -1600,7 +1634,12 @@ __PACKAGE__->register_method({ my $websocket = $param->{websocket}; my $conf = PVE::QemuConfig->load_config($vmid, $node); # check if VM exists - my $use_serial = ($conf->{vga} && ($conf->{vga} =~ m/^serial\d+$/)); + + my $serial; + if ($conf->{vga}) { + my $vga = PVE::QemuServer::parse_vga($conf->{vga}); + $serial = $vga->{type} if $vga->{type} =~ m/^serial\d+$/; + } my $authpath = "/vms/$vmid"; @@ -1616,7 +1655,7 @@ __PACKAGE__->register_method({ (undef, $family) = PVE::Cluster::remote_node_ip($node); my $sshinfo = PVE::SSHInfo::get_ssh_info($node); # NOTE: kvm VNC traffic is already TLS encrypted or is known unsecure - $remcmd = PVE::SSHInfo::ssh_info_to_command($sshinfo, $use_serial ? '-t' : '-T'); + $remcmd = PVE::SSHInfo::ssh_info_to_command($sshinfo, defined($serial) ? '-t' : '-T'); } else { $family = PVE::Tools::get_host_address_family($node); } @@ -1632,9 +1671,9 @@ __PACKAGE__->register_method({ my $cmd; - if ($use_serial) { + if (defined($serial)) { - my $termcmd = [ '/usr/sbin/qm', 'terminal', $vmid, '-iface', $conf->{vga}, '-escape', '0' ]; + my $termcmd = [ '/usr/sbin/qm', 'terminal', $vmid, '-iface', $serial, '-escape', '0' ]; $cmd = ['/usr/bin/vncterm', '-rfbport', $port, '-timeout', $timeout, '-authpath', $authpath, @@ -2589,6 +2628,8 @@ __PACKAGE__->register_method({ if $skiplock && $authuser ne 'root@pam'; my $nocheck = extract_param($param, 'nocheck'); + raise_param_exc({ nocheck => "Only root may use this option." }) + if $nocheck && $authuser ne 'root@pam'; my $to_disk_suspended; eval { @@ -2850,9 +2891,6 @@ __PACKAGE__->register_method({ my $running = PVE::QemuServer::check_running($vmid) || 0; - # exclusive lock if VM is running - else shared lock is enough; - my $shared_lock = $running ? 0 : 1; - my $clonefn = sub { # do all tests after lock but before forking worker - if possible @@ -3040,11 +3078,17 @@ __PACKAGE__->register_method({ return $rpcenv->fork_worker('qmclone', $vmid, $authuser, $realcmd); }; - return PVE::QemuConfig->lock_config_mode($vmid, 1, $shared_lock, sub { - # Aquire exclusive lock lock for $newid + # Aquire exclusive lock lock for $newid + my $lock_target_vm = sub { return PVE::QemuConfig->lock_config_full($newid, 1, $clonefn); - }); + }; + # exclusive lock if VM is running - else shared lock is enough; + if ($running) { + return PVE::QemuConfig->lock_config_full($vmid, 1, $lock_target_vm); + } else { + return PVE::QemuConfig->lock_config_shared($vmid, 1, $lock_target_vm); + } }}); __PACKAGE__->register_method({ @@ -3455,9 +3499,6 @@ __PACKAGE__->register_method({ $param->{online} = 0; } - raise_param_exc({ targetstorage => "Live storage migration can only be done online." }) - if !$param->{online} && $param->{targetstorage}; - my $storecfg = PVE::Storage::config(); if (my $targetstorage = $param->{targetstorage}) { @@ -3687,8 +3728,7 @@ __PACKAGE__->register_method({ PVE::QemuServer::qemu_block_resize($vmid, "drive-$disk", $storecfg, $volid, $newsize); - my $effective_size = eval { PVE::Storage::volume_size_info($storecfg, $volid, 3); }; - $drive->{size} = $effective_size // $newsize; + $drive->{size} = $newsize; $conf->{$disk} = PVE::QemuServer::print_drive($drive); PVE::QemuConfig->write_config($vmid, $conf);