]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
clone_vm: auto generate new uuid
[qemu-server.git] / PVE / API2 / Qemu.pm
index ebacc95cecefd58ca0517eaf5f2680f06c5d53fa..985a9f8c1caace2d127f6d9c11bca7b4f5dc620e 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 use Cwd 'abs_path';
 use Net::SSLeay;
+use UUID;
 
 use PVE::Cluster qw (cfs_read_file cfs_write_file);;
 use PVE::SafeSyslog;
@@ -192,7 +193,7 @@ my $check_vm_modify_config_perm = sub {
        } elsif ($opt eq 'args' || $opt eq 'lock') {
            die "only root can set '$opt' config\n";
        } elsif ($opt eq 'cpu' || $opt eq 'kvm' || $opt eq 'acpi' || $opt eq 'machine' ||
-                $opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet') {
+                $opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet' || $opt eq 'smbios1') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
        } elsif ($opt =~ m/^net\d+$/) {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
@@ -431,6 +432,14 @@ __PACKAGE__->register_method({
                        $conf->{bootdisk} = $firstdisk;
                    }
 
+                   # auto generate uuid if user did not specify smbios1 option
+                   if (!$conf->{smbios1}) {
+                       my ($uuid, $uuid_str);
+                       UUID::generate($uuid);
+                       UUID::unparse($uuid, $uuid_str);
+                       $conf->{smbios1} = "uuid=$uuid_str";
+                   }
+
                    PVE::QemuServer::update_config_nolock($vmid, $conf);
 
                };
@@ -1270,11 +1279,6 @@ __PACKAGE__->register_method({
        properties => {
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid'),
-           unsecure => {
-               optional => 1,
-               type => 'boolean',
-               description => "disables x509 auth",
-           },
            websocket => {
                optional => 1,
                type => 'boolean',
@@ -1301,8 +1305,7 @@ __PACKAGE__->register_method({
 
        my $vmid = $param->{vmid};
        my $node = $param->{node};
-       my $unsecure = $param->{unsecure} // 0;
-       my $websocket = $param->{websocket} // 0;
+       my $websocket = $param->{websocket};
 
        my $conf = PVE::QemuServer::load_config($vmid, $node); # check if VM exists
 
@@ -1335,7 +1338,7 @@ __PACKAGE__->register_method({
 
            if ($conf->{vga} && ($conf->{vga} =~ m/^serial\d+$/)) {
 
-               die "Unsecure mode is not supported in vga serial mode!" if $unsecure;
+               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}";
@@ -1344,39 +1347,7 @@ __PACKAGE__->register_method({
                        '-perm', 'Sys.Console', '-c', @$remcmd, @$termcmd];
            } else {
 
-               my $vnc_socket = PVE::QemuServer::vnc_socket($vmid);
-
-               if (defined $remip) {
-                   my $perlcode = "";
-                   if ($unsecure) {
-                       $perlcode = qq|
-                               use PVE::QemuServer;
-
-                               PVE::QemuServer::vm_mon_cmd($vmid, "change", device => "vnc", target => "unix:$vnc_socket,password");
-
-                               PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => "vnc", password => "$ticket");
-
-                               PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => "vnc", time => "+30");
-                               |;
-                   } else {
-                       $perlcode = qq|
-                               use PVE::QemuServer;
-
-                               PVE::QemuServer::vm_mon_cmd($vmid, "change", device => "vnc", target => "unix:$vnc_socket,x509,password");
-                               |;
-                   }
-
-                   PVE::Tools::run_command([@$remcmd, 'perl', '-'], input => $perlcode, outfunc => sub {print shift;}, errfunc => sub {print STDERR shift;});
-
-               } else {
-                   if ($unsecure) {
-                       PVE::QemuServer::vm_mon_cmd($vmid, "change", device => 'vnc', target => "unix:$vnc_socket,password");
-                       PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => 'vnc', password => $ticket);
-                       PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => 'vnc', time => "+30");
-                   } else {
-                       PVE::QemuServer::vm_mon_cmd($vmid, "change", device => 'vnc', target => "unix:$vnc_socket,x509,password");
-                   }
-               }
+               $ENV{LC_PVE_TICKET} = $ticket if $websocket; # set ticket with "qm vncproxy"
 
                my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
 
@@ -1384,10 +1355,6 @@ __PACKAGE__->register_method({
 
                # also redirect stderr (else we get RFB protocol errors)
                $cmd = ['/bin/nc', '-l', '-p', $port, '-w', $timeout, '-c', "$qmstr 2>/dev/null"];
-
-               if ($websocket) {
-                   $cmd = ["/usr/share/novnc/utils/wsproxy.py", '--run-once', "--timeout=$timeout", "--idle-timeout=$timeout", '--ssl-only', '--cert', '/etc/pve/local/pve-ssl.pem', '--key', '/etc/pve/local/pve-ssl.key', $port, '--', @$cmd];
-               }
            }
 
            PVE::Tools::run_command($cmd);
@@ -1408,6 +1375,64 @@ __PACKAGE__->register_method({
        };
     }});
 
+__PACKAGE__->register_method({
+    name => 'vncwebsocket',
+    path => '{vmid}/vncwebsocket',
+    method => 'GET',
+    permissions => {
+       description => "You also need to pass a valid ticket (vncticket).",
+       check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
+    },
+    description => "Opens a weksocket for VNC traffic.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           vmid => get_standard_option('pve-vmid'),
+           vncticket => {
+               description => "Ticket from previous call to vncproxy.",
+               type => 'string',
+               maxLength => 512,
+           },
+           port => {
+               description => "Port number returned by previous vncproxy call.",
+               type => 'integer',
+               minimum => 5900,
+               maximum => 5999,
+           },
+       },
+    },
+    returns => {
+       type => "object",
+       properties => {
+           port => { type => 'string' },
+       },
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+
+       my $authuser = $rpcenv->get_user();
+
+       my $vmid = $param->{vmid};
+       my $node = $param->{node};
+
+       my $authpath = "/vms/$vmid";
+
+       PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
+
+       my $conf = PVE::QemuServer::load_config($vmid, $node); # VM exists ?
+
+       # 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 = $param->{port};
+       
+       return { port => $port };
+    }});
+
 __PACKAGE__->register_method({
     name => 'spiceproxy',
     path => '{vmid}/spiceproxy',
@@ -1438,8 +1463,8 @@ __PACKAGE__->register_method({
        my $node = $param->{node};
        my $proxy = $param->{proxy};
 
-    my $conf = PVE::QemuServer::load_config($vmid, $node);
-    my $title = "VM $vmid - $conf->{'name'}",
+       my $conf = PVE::QemuServer::load_config($vmid, $node);
+       my $title = "VM $vmid - $conf->{'name'}",
 
        my $port = PVE::QemuServer::spice_port($vmid);
 
@@ -1649,7 +1674,7 @@ __PACKAGE__->register_method({
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid'),
            skiplock => get_standard_option('skiplock'),
-           migratedfrom => get_standard_option('pve-node',{ optional => 1 }),
+           migratedfrom => get_standard_option('pve-node', { optional => 1 }),
            timeout => {
                description => "Wait maximal timeout seconds.",
                type => 'integer',
@@ -1693,7 +1718,7 @@ __PACKAGE__->register_method({
 
        my $storecfg = PVE::Storage::config();
 
-       if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
+       if (&$vm_is_ha_managed($vmid) && ($rpcenv->{type} ne 'ha') && !defined($migratedfrom)) {
 
            my $hacmd = sub {
                my $upid = shift;
@@ -2103,7 +2128,6 @@ __PACKAGE__->register_method({
                description => "Add the new VM to the specified pool.",
            },
             snapname => get_standard_option('pve-snapshot-name', {
-               requires => 'full',
                optional => 1,
             }),
            storage => get_standard_option('pve-storage-id', {
@@ -2237,10 +2261,14 @@ __PACKAGE__->register_method({
                    if (PVE::QemuServer::drive_is_cdrom($drive)) {
                        $newconf->{$opt} = $value; # simply copy configuration
                    } else {
-                       if ($param->{full} || !PVE::Storage::volume_is_base($storecfg,  $drive->{file})) {
+                       if ($param->{full}) {
                            die "Full clone feature is not available"
                                if !PVE::Storage::volume_has_feature($storecfg, 'copy', $drive->{file}, $snapname, $running);
                            $drive->{full} = 1;
+                       } else {
+                           # not full means clone instead of copy
+                           die "Linked clone feature is not available"
+                               if !PVE::Storage::volume_has_feature($storecfg, 'clone', $drive->{file}, $snapname, $running);
                        }
                        $drives->{$opt} = $drive;
                        push @$vollist, $drive->{file};
@@ -2251,6 +2279,14 @@ __PACKAGE__->register_method({
                }
            }
 
+            # auto generate a new uuid
+            my ($uuid, $uuid_str);
+            UUID::generate($uuid);
+            UUID::unparse($uuid, $uuid_str);
+           my $smbios1 = PVE::QemuServer::parse_smbios1($newconf->{smbios1} || '');
+           $smbios1->{uuid} = $uuid_str; 
+           $newconf->{smbios1} = PVE::QemuServer::print_smbios1($smbios1);
+
            delete $newconf->{template};
 
            if ($param->{name}) {
@@ -2699,9 +2735,6 @@ __PACKAGE__->register_method({
 
            die "you can't resize a cdrom\n" if PVE::QemuServer::drive_is_cdrom($drive);
 
-           die "you can't online resize a virtio windows bootdisk\n"
-               if PVE::QemuServer::check_running($vmid) && $conf->{bootdisk} eq $disk && $conf->{ostype} =~ m/^w/ && $disk =~ m/^virtio/;
-
            my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
 
            $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);