]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
Allow VMAdmin to delete disk with Datastore.AllocateSpace permissions
[qemu-server.git] / PVE / API2 / Qemu.pm
index 5d9cbb92cfec0f1be4ba380dd1980ae79374dfeb..bf16bf23c2c4b435ddfe5e410f04142a82251dd9 100644 (file)
@@ -3,6 +3,7 @@ package PVE::API2::Qemu;
 use strict;
 use warnings;
 use Cwd 'abs_path';
+use Net::SSLeay;
 
 use PVE::Cluster qw (cfs_read_file cfs_write_file);;
 use PVE::SafeSyslog;
@@ -696,7 +697,7 @@ 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($authuser, "/storage/$sid", ['Datastore.Allocate']);
+           $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
        }
     }
 
@@ -1272,6 +1273,8 @@ __PACKAGE__->register_method({
        my $vmid = $param->{vmid};
        my $node = $param->{node};
 
+       my $conf = PVE::QemuServer::load_config($vmid, $node); # check if VM exists
+
        my $authpath = "/vms/$vmid";
 
        my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
@@ -1282,14 +1285,14 @@ __PACKAGE__->register_method({
        my $port = PVE::Tools::next_vnc_port();
 
        my $remip;
+       my $remcmd = [];
 
        if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
            $remip = PVE::Cluster::remote_node_ip($node);
+           # NOTE: kvm VNC traffic is already TLS encrypted
+           $remcmd = ['/usr/bin/ssh', '-T', '-o', 'BatchMode=yes', $remip];
        }
 
-       # NOTE: kvm VNC traffic is already TLS encrypted
-       my $remcmd = $remip ? ['/usr/bin/ssh', '-T', '-o', 'BatchMode=yes', $remip] : [];
-
        my $timeout = 10;
 
        my $realcmd = sub {
@@ -1297,12 +1300,24 @@ __PACKAGE__->register_method({
 
            syslog('info', "starting vnc proxy $upid\n");
 
-           my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
+           my $cmd;
+
+           if ($conf->{vga} && ($conf->{vga} =~ m/^serial\d+$/)) {
+
+               my $termcmd = [ '/usr/sbin/qm', 'terminal', $vmid, '-iface', $conf->{vga} ];
+               #my $termcmd = "/usr/bin/qm terminal -iface $conf->{vga}";
+               $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
+                       '-timeout', $timeout, '-authpath', $authpath, 
+                       '-perm', 'Sys.Console', '-c', @$remcmd, @$termcmd];
+           } else {
 
-           my $qmstr = join(' ', @$qmcmd);
+               my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
 
-           # also redirect stderr (else we get RFB protocol errors)
-           my $cmd = ['/bin/nc', '-l', '-p', $port, '-w', $timeout, '-c', "$qmstr 2>/dev/null"];
+               my $qmstr = join(' ', @$qmcmd);
+
+               # also redirect stderr (else we get RFB protocol errors)
+               $cmd = ['/bin/nc', '-l', '-p', $port, '-w', $timeout, '-c', "$qmstr 2>/dev/null"];
+           }
 
            PVE::Tools::run_command($cmd);
 
@@ -1325,7 +1340,7 @@ __PACKAGE__->register_method({
 __PACKAGE__->register_method({
     name => 'spiceproxy',
     path => '{vmid}/spiceproxy',
-    method => 'GET', # fixme: should be POST, but howto handle that in the HTML client
+    method => 'GET',
     protected => 1,
     proxyto => 'node', # fixme: use direct connections or ssh tunnel?
     permissions => {
@@ -1337,15 +1352,22 @@ __PACKAGE__->register_method({
        properties => {
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid'),
+           proxy => {
+               description => "This can be used by the client to specify the proxy server. All nodes in a cluster runs 'spiceproxy', so it is up to the client to choose one. By default, we return the node where the VM is currently running. As resonable setting is to use same node you use to connect to the API (This is window.location.hostname for the JS GUI).",
+               type => 'string', format => 'dns-name',
+               optional => 1,
+           },
        },
     },
     returns => {
+       description => "Returned values can be directly passed to the 'remote-viewer' application.",
        additionalProperties => 1,
        properties => {
            type => { type => 'string' },
            password => { type => 'string' },
+           proxy => { type => 'string' },
            host => { type => 'string' },
-           port => { type => 'integer' },
+           'tls-port' => { type => 'integer' },
        },
     },
     code => sub {
@@ -1357,82 +1379,38 @@ __PACKAGE__->register_method({
 
        my $vmid = $param->{vmid};
        my $node = $param->{node};
+       my $proxy = $param->{proxy};
 
-       my $port = PVE::Tools::next_vnc_port();
-
-        my $remip;
-
-       # Note: we currectly use "proxyto => 'node'", so this code will never trigger
-        if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
-            $remip = PVE::Cluster::remote_node_ip($node);
-        }
-
-       my $authpath = "/vms/$vmid";
-
-       my $ticket = PVE::AccessControl::assemble_spice_ticket($authuser, $authpath);
-
-       # limit ticket length to 59 charachters
-       $ticket = substr($ticket, 0, 59);
+       my ($ticket, $proxyticket) = PVE::AccessControl::assemble_spice_ticket($authuser, $vmid, $node);
 
        my $timeout = 10;
 
-       # Note: this only works if VM is on local node
+       my $port = PVE::QemuServer::spice_port($vmid);
        PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => 'spice', password => $ticket);
        PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
 
-       my $remcmd = []; #fixme
-
-       my $realcmd = sub {
-           my $upid = shift;
-
-           syslog('info', "starting spice proxy $upid\n");
-       
-           my $socket = PVE::QemuServer::spice_socket($vmid);
-       
-           my $cmd = ['/usr/bin/socat', '-d', '-d', 
-                      "TCP-LISTEN:$port,reuseaddr,fork" ];
-
-           if ($remip) {
-               push @$cmd, "EXEC:'ssh root@$remip socat STDIO UNIX-CONNECT:$socket";
-           } else {
-               push @$cmd, "UNIX-CONNECT:$socket";
-           }
-
-           my $conn_count = 0;
-
-           my $parser = sub {
-               my $line = shift;
-               print "$line\n";
-               if ($line =~ /successfully connected from/) {
-                   $conn_count++;
-               } elsif ($line =~ /N exiting with status/ || $line =~ m/N exit\(/) {
-                   $conn_count--;
-                   die "client exit\n" if $conn_count <= 0;
-               }
-           };
-           
-           eval { PVE::Tools::run_command($cmd, errfunc => $parser, outfunc => sub{}); };
-           if (my $err = $@) {
-               die $err if $err !~ m/client exit$/;
-           }
-
-           return;
-       };
-
-       my $upid = $rpcenv->fork_worker('spiceproxy', $vmid, $authuser, $realcmd);
+       if (!$proxy) {
+           my $host = `hostname -f` || PVE::INotify::nodename();
+           chomp $host;
+           $proxy = $host;
+       }
 
-       PVE::Tools::wait_for_vnc_port($port);
+       my $filename = "/etc/pve/local/pve-ssl.pem";
+       my $subject = PVE::QemuServer::read_x509_subject_spice($filename);
 
-       # fimxe: ??
-       my $host = `hostname -f` || PVE::INotify::nodename();
-       chomp $host;
+       my $cacert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192);
+       $cacert =~ s/\n/\\n/g;
 
        return {
            type => 'spice',
-           host => $host,
-           port => $port,
+           title => "VM $vmid",
+           host => $proxyticket, # this break tls hostname verification, so we need to use 'host-subject'
+           proxy => "http://$proxy:3128",
+           'tls-port' => $port,
+           'host-subject' => $subject,
+           ca => $cacert,
            password => $ticket,
-           upid => $upid,
+           'delete-this-file' => 1,
        };
     }});
 
@@ -1516,6 +1494,8 @@ __PACKAGE__->register_method({
 
        $status->{ha} = &$vm_is_ha_managed($param->{vmid});
 
+       $status->{spice} = 1 if PVE::QemuServer::vga_conf_has_spice($conf->{vga});
+
        return $status;
     }});
 
@@ -1568,6 +1548,15 @@ __PACKAGE__->register_method({
        raise_param_exc({ migratedfrom => "Only root may use this option." })
            if $migratedfrom && $authuser ne 'root@pam';
 
+       # read spice ticket from STDIN
+       my $spice_ticket;
+       if ($stateuri && ($stateuri eq 'tcp') && $migratedfrom && ($rpcenv->{type} eq 'cli')) {
+           if (defined(my $line = <>)) {
+               chomp $line;
+               $spice_ticket = $line;
+           }
+       }
+
        my $storecfg = PVE::Storage::config();
 
        if (&$vm_is_ha_managed($vmid) && !$stateuri &&
@@ -1596,7 +1585,8 @@ __PACKAGE__->register_method({
 
                syslog('info', "start VM $vmid: $upid\n");
 
-               PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock, $migratedfrom, undef, $machine);
+               PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock, $migratedfrom, undef, 
+                                         $machine, $spice_ticket);
 
                return;
            };