]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
add 'lsi53c810' to the list of scsi controllers
[qemu-server.git] / PVE / API2 / Qemu.pm
index ba88f72b9fc1817913ece101e6391a16dc68573a..7bce9e2b91e85ef4b1934b845f567288b65cccc9 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;
@@ -126,7 +127,7 @@ my $create_disks = sub {
            $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
        } else {
 
-           my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
+           $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
 
            my $volid_is_new = 1;
 
@@ -371,13 +372,8 @@ __PACKAGE__->register_method({
                die "pipe requires cli environment\n"
                    if $rpcenv->{type} ne 'cli';
            } else {
-               my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
-
-               PVE::Storage::activate_volumes($storecfg, [ $archive ])
-                   if PVE::Storage::parse_volume_id ($archive, 1);
-
-               die "can't find archive file '$archive'\n" if !($path && -f $path);
-               $archive = $path;
+               $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
+               $archive = PVE::Storage::abs_filesystem_path($storecfg, $archive);
            }
        }
 
@@ -498,6 +494,7 @@ __PACKAGE__->register_method({
            { subdir => 'monitor' },
            { subdir => 'snapshot' },
            { subdir => 'spiceproxy' },
+           { subdir => 'sendkey' },
            ];
 
        return $res;
@@ -696,22 +693,22 @@ 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']);
        }
     }
 
     my $unplugwarning = "";
-    if($conf->{ostype} && $conf->{ostype} eq 'l26'){
+    if ($conf->{ostype} && $conf->{ostype} eq 'l26') {
        $unplugwarning = "<br>verify that you have acpiphp && pci_hotplug modules loaded in your guest VM";
-    }elsif($conf->{ostype} && $conf->{ostype} eq 'l24'){
+    } elsif ($conf->{ostype} && $conf->{ostype} eq 'l24') {
        $unplugwarning = "<br>kernel 2.4 don't support hotplug, please disable hotplug in options";
-    }elsif(!$conf->{ostype} || ($conf->{ostype} && $conf->{ostype} eq 'other')){
+    } elsif (!$conf->{ostype} || ($conf->{ostype} && $conf->{ostype} eq 'other')) {
        $unplugwarning = "<br>verify that your guest support acpi hotplug";
     }
 
-    if($opt eq 'tablet'){
+    if ($opt eq 'tablet') {
        PVE::QemuServer::vm_deviceplug(undef, $conf, $vmid, $opt);
-    }else{
+    } else {
         die "error hot-unplug $opt $unplugwarning" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
     }
 
@@ -767,12 +764,12 @@ my $vmconfig_update_disk = sub {
                &$safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
                &$safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
                &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr})) {
-               PVE::QemuServer::qemu_block_set_io_throttle($vmid,"drive-$opt", 
+               PVE::QemuServer::qemu_block_set_io_throttle($vmid,"drive-$opt",
                                                           ($drive->{mbps} || 0)*1024*1024,
-                                                          ($drive->{mbps_rd} || 0)*1024*1024, 
+                                                          ($drive->{mbps_rd} || 0)*1024*1024,
                                                           ($drive->{mbps_wr} || 0)*1024*1024,
-                                                          $drive->{iops} || 0, 
-                                                          $drive->{iops_rd} || 0, 
+                                                          $drive->{iops} || 0,
+                                                          $drive->{iops_rd} || 0,
                                                           $drive->{iops_wr} || 0)
                   if !PVE::QemuServer::drive_is_cdrom($drive);
             }
@@ -1272,6 +1269,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 +1281,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 +1296,24 @@ __PACKAGE__->register_method({
 
            syslog('info', "starting vnc proxy $upid\n");
 
-           my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
+           my $cmd;
 
-           my $qmstr = join(' ', @$qmcmd);
+           if ($conf->{vga} && ($conf->{vga} =~ m/^serial\d+$/)) {
 
-           # also redirect stderr (else we get RFB protocol errors)
-           my $cmd = ['/bin/nc', '-l', '-p', $port, '-w', $timeout, '-c', "$qmstr 2>/dev/null"];
+               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 $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
+
+               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);
 
@@ -1337,16 +1348,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 {
@@ -1358,39 +1375,38 @@ __PACKAGE__->register_method({
 
        my $vmid = $param->{vmid};
        my $node = $param->{node};
-
-        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 $proxy = $param->{proxy};
 
        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");
 
-       # allow access for group www-data to the spice socket,
-       # so that spiceproxy can access it
-       my $socket =  PVE::QemuServer::spice_socket($vmid);
-       my $gid = getgrnam('www-data') || die "getgrnam failed - $!\n";
-       chown 0, $gid, $socket;
-       chmod 0770, $socket;
+       if (!$proxy) {
+           my $host = `hostname -f` || PVE::INotify::nodename();
+           chomp $host;
+           $proxy = $host;
+       }
+
+       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 => $proxyticket,
-           proxy => "http://$host:3128",
-           port => 0, # not used for now
-           password => $ticket
+           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,
+           'delete-this-file' => 1,
        };
     }});
 
@@ -1474,9 +1490,7 @@ __PACKAGE__->register_method({
 
        $status->{ha} = &$vm_is_ha_managed($param->{vmid});
 
-       if ($conf->{vga} && ($conf->{vga} eq 'qxl')) {
-           $status->{spice} = 1;
-       }
+       $status->{spice} = 1 if PVE::QemuServer::vga_conf_has_spice($conf->{vga});
 
        return $status;
     }});
@@ -1530,6 +1544,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 &&
@@ -1558,7 +1581,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;
            };
@@ -2165,7 +2189,9 @@ __PACKAGE__->register_method({
                    my $net = PVE::QemuServer::parse_net($value);
                    $net->{macaddr} =  PVE::Tools::random_ether_addr();
                    $newconf->{$opt} = PVE::QemuServer::print_net($net);
-               } elsif (my $drive = PVE::QemuServer::parse_drive($opt, $value)) {
+               } elsif (PVE::QemuServer::valid_drivename($opt)) {
+                   my $drive = PVE::QemuServer::parse_drive($opt, $value);
+                   die "unable to parse drive options for '$opt'\n" if !$drive;
                    if (PVE::QemuServer::drive_is_cdrom($drive)) {
                        $newconf->{$opt} = $value; # simply copy configuration
                    } else {
@@ -2227,6 +2253,9 @@ __PACKAGE__->register_method({
                    PVE::QemuServer::update_config_nolock($newid, $newconf, 1);
 
                     if ($target) {
+                       # always deactivate volumes - avoid lvm LVs to be active on several nodes
+                       PVE::Storage::deactivate_volumes($storecfg, $vollist);
+
                        my $newconffile = PVE::QemuServer::config_file($newid, $target);
                        die "Failed to move config to node '$target' - rename failed: $!\n"
                            if !rename($conffile, $newconffile);