]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
do not use novnc wsproxy
[qemu-server.git] / PVE / API2 / Qemu.pm
index 456076866f1711c94dc2241fdbad529764856cb5..2f07ae57449565400afb9c4a34d9083459fba1e6 100644 (file)
@@ -18,6 +18,7 @@ use PVE::RPCEnvironment;
 use PVE::AccessControl;
 use PVE::INotify;
 use PVE::Network;
+use PVE::API2::Firewall::VM;
 
 use Data::Dumper; # fixme: remove
 
@@ -495,11 +496,17 @@ __PACKAGE__->register_method({
            { subdir => 'snapshot' },
            { subdir => 'spiceproxy' },
            { subdir => 'sendkey' },
+           { subdir => 'firewall' },
            ];
 
        return $res;
     }});
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::VM",  
+    path => '{vmid}/firewall',
+});
+
 __PACKAGE__->register_method({
     name => 'rrd',
     path => '{vmid}/rrd',
@@ -831,9 +838,9 @@ my $vmconfig_update_net = sub {
                    PVE::Network::tap_rate_limit($iface, $newnet->{rate});
                }
 
-               if(($newnet->{bridge} ne $oldnet->{bridge}) || ($newnet->{tag} ne $oldnet->{tag})){
-                   eval{PVE::Network::tap_unplug($iface, $oldnet->{bridge}, $oldnet->{tag});};
-                   PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag});
+               if(($newnet->{bridge} ne $oldnet->{bridge}) || ($newnet->{tag} ne $oldnet->{tag}) || ($newnet->{firewall} ne $oldnet->{firewall})){
+                   PVE::Network::tap_unplug($iface);
+                   PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
                }
 
            }else{
@@ -988,6 +995,10 @@ my $update_vm_api  = sub {
                    } elsif($opt eq 'tablet' && $param->{$opt} == 0){
                        PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
                    }
+               
+                   if($opt eq 'cores' && $conf->{maxcpus}){
+                       PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $param->{$opt});
+                   }
 
                    $conf->{$opt} = $param->{$opt};
                    PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
@@ -1259,6 +1270,11 @@ __PACKAGE__->register_method({
        properties => {
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid'),
+           websocket => {
+               optional => 1,
+               type => 'boolean',
+               description => "starts websockify instead of vncproxy",
+           },
        },
     },
     returns => {
@@ -1280,6 +1296,7 @@ __PACKAGE__->register_method({
 
        my $vmid = $param->{vmid};
        my $node = $param->{node};
+       my $websocket = $param->{websocket};
 
        my $conf = PVE::QemuServer::load_config($vmid, $node); # check if VM exists
 
@@ -1297,7 +1314,7 @@ __PACKAGE__->register_method({
 
        if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
            $remip = PVE::Cluster::remote_node_ip($node);
-           # NOTE: kvm VNC traffic is already TLS encrypted
+           # NOTE: kvm VNC traffic is already TLS encrypted or is known unsecure
            $remcmd = ['/usr/bin/ssh', '-T', '-o', 'BatchMode=yes', $remip];
        }
 
@@ -1312,6 +1329,8 @@ __PACKAGE__->register_method({
 
            if ($conf->{vga} && ($conf->{vga} =~ m/^serial\d+$/)) {
 
+               die "Websocket mode is not supported in vga serial mode!" if $websocket;
+
                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,
@@ -1319,6 +1338,8 @@ __PACKAGE__->register_method({
                        '-perm', 'Sys.Console', '-c', @$remcmd, @$termcmd];
            } else {
 
+               $ENV{LC_PVE_TICKET} = $ticket if $websocket; # set ticket with "qm vncproxy"
+
                my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
 
                my $qmstr = join(' ', @$qmcmd);
@@ -1346,36 +1367,31 @@ __PACKAGE__->register_method({
     }});
 
 __PACKAGE__->register_method({
-    name => 'spiceproxy',
-    path => '{vmid}/spiceproxy',
-    method => 'POST',
-    protected => 1,
+    name => 'vncwebsocket',
+    path => '{vmid}/vncwebsocket',
+    method => 'GET',
     proxyto => 'node',
     permissions => {
        check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
     },
-    description => "Returns a SPICE configuration to connect to the VM.",
+    description => "Opens a weksocket for VNV traffic.",
     parameters => {
        additionalProperties => 0,
        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,
+           port => {
+               description => "Port number returned by previous vncproxy call.",
+               type => 'integer',
+               minimum => 5900,
+               maximum => 5999,
            },
        },
     },
     returns => {
-       description => "Returned values can be directly passed to the 'remote-viewer' application.",
-       additionalProperties => 1,
+       type => "object",
        properties => {
-           type => { type => 'string' },
-           password => { type => 'string' },
-           proxy => { type => 'string' },
-           host => { type => 'string' },
-           'tls-port' => { type => 'integer' },
+           port => { type => 'string' },
        },
     },
     code => sub {
@@ -1387,39 +1403,60 @@ __PACKAGE__->register_method({
 
        my $vmid = $param->{vmid};
        my $node = $param->{node};
-       my $proxy = $param->{proxy};
 
-       my ($ticket, $proxyticket) = PVE::AccessControl::assemble_spice_ticket($authuser, $vmid, $node);
+       my $conf = PVE::QemuServer::load_config($vmid, $node); # VM exists ?
 
-       my $timeout = 10;
+       # Note: VNC ports are acessible from outside, so we do not gain any
+       # security if we verify that $param->{port} belongs to VM $vmid. This
+       # check is done by verifying the VNC ticket (inside VNC protocol).
 
-       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 $port = $param->{port};
+       
+       return { port => $port };
+    }});
 
-       if (!$proxy) {
-           my $host = `hostname -f` || PVE::INotify::nodename();
-           chomp $host;
-           $proxy = $host;
-       }
+__PACKAGE__->register_method({
+    name => 'spiceproxy',
+    path => '{vmid}/spiceproxy',
+    method => 'POST',
+    protected => 1,
+    proxyto => 'node',
+    permissions => {
+       check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
+    },
+    description => "Returns a SPICE configuration to connect to the VM.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           vmid => get_standard_option('pve-vmid'),
+           proxy => get_standard_option('spice-proxy', { optional => 1 }),
+       },
+    },
+    returns => get_standard_option('remote-viewer-config'),
+    code => sub {
+       my ($param) = @_;
 
-       my $filename = "/etc/pve/local/pve-ssl.pem";
-       my $subject = PVE::QemuServer::read_x509_subject_spice($filename);
+       my $rpcenv = PVE::RPCEnvironment::get();
 
-       my $cacert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192);
-       $cacert =~ s/\n/\\n/g;
+       my $authuser = $rpcenv->get_user();
 
-       return {
-           type => 'spice',
-           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,
-       };
+       my $vmid = $param->{vmid};
+       my $node = $param->{node};
+       my $proxy = $param->{proxy};
+
+       my $conf = PVE::QemuServer::load_config($vmid, $node);
+       my $title = "VM $vmid - $conf->{'name'}",
+
+       my $port = PVE::QemuServer::spice_port($vmid);
+
+       my ($ticket, undef, $remote_viewer_config) = 
+           PVE::AccessControl::remote_viewer_config($authuser, $vmid, $node, $proxy, $title, $port);
+       
+       PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => 'spice', password => $ticket);
+       PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
+       
+       return $remote_viewer_config;
     }});
 
 __PACKAGE__->register_method({
@@ -2417,6 +2454,13 @@ __PACKAGE__->register_method({
                    PVE::QemuServer::add_unused_volume($conf, $old_volid) if !$param->{delete};
 
                    PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+
+                   eval { 
+                       # try to deactivate volumes - avoid lvm LVs to be active on several nodes
+                       PVE::Storage::deactivate_volumes($storecfg, [ $newdrive->{file} ]) 
+                           if !$running;
+                   };
+                   warn $@ if $@;
                };
                if (my $err = $@) {
 
@@ -2428,8 +2472,16 @@ __PACKAGE__->register_method({
                 }
 
                if ($param->{delete}) {
-                   eval { PVE::Storage::vdisk_free($storecfg, $old_volid); };
-                   warn $@ if $@;
+                    my $used_paths = PVE::QemuServer::get_used_paths($vmid, $storecfg, $conf, 1, 1);
+                    my $path = PVE::Storage::path($storecfg, $old_volid);
+                   if ($used_paths->{$path}){
+                       warn "volume $old_volid have snapshots. Can't delete it\n";
+                       PVE::QemuServer::add_unused_volume($conf, $old_volid);
+                       PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+                   } else {
+                       eval { PVE::Storage::vdisk_free($storecfg, $old_volid); };
+                       warn $@ if $@;
+                   }
                }
            };