]> 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 2c864879e0b16c2f669391cdd97120e051f9d8df..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;
@@ -18,6 +19,7 @@ use PVE::RPCEnvironment;
 use PVE::AccessControl;
 use PVE::INotify;
 use PVE::Network;
+use PVE::API2::Firewall::VM;
 
 use Data::Dumper; # fixme: remove
 
@@ -127,7 +129,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;
 
@@ -191,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']);
@@ -372,13 +374,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);
            }
        }
 
@@ -435,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);
 
                };
@@ -499,11 +504,18 @@ __PACKAGE__->register_method({
            { subdir => 'monitor' },
            { 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',
@@ -697,22 +709,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,14 +779,26 @@ my $vmconfig_update_disk = sub {
                &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
                &$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", 
+               &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
+               &$safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
+               &$safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
+               &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
+               &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
+               &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
+               &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max})) {
+               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_wr} || 0)
+                                                          $drive->{iops} || 0,
+                                                          $drive->{iops_rd} || 0,
+                                                          $drive->{iops_wr} || 0,
+                                                          ($drive->{mbps_max} || 0)*1024*1024,
+                                                          ($drive->{mbps_rd_max} || 0)*1024*1024,
+                                                          ($drive->{mbps_wr_max} || 0)*1024*1024,
+                                                          $drive->{iops_max} || 0,
+                                                          $drive->{iops_rd_max} || 0,
+                                                          $drive->{iops_wr_max} || 0)
                   if !PVE::QemuServer::drive_is_cdrom($drive);
             }
        }
@@ -823,9 +847,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{
@@ -980,6 +1004,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);
@@ -1251,6 +1279,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 => {
@@ -1272,6 +1305,9 @@ __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
 
        my $authpath = "/vms/$vmid";
 
@@ -1283,14 +1319,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 or is known unsecure
+           $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 {
@@ -1298,12 +1334,28 @@ __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 $qmstr = join(' ', @$qmcmd);
+               die "Websocket mode is not supported in vga serial mode!" if $websocket;
 
-           # 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 {
+
+               $ENV{LC_PVE_TICKET} = $ticket if $websocket; # set ticket with "qm vncproxy"
+
+               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);
 
@@ -1324,36 +1376,36 @@ __PACKAGE__->register_method({
     }});
 
 __PACKAGE__->register_method({
-    name => 'spiceproxy',
-    path => '{vmid}/spiceproxy',
+    name => 'vncwebsocket',
+    path => '{vmid}/vncwebsocket',
     method => 'GET',
-    protected => 1,
-    proxyto => 'node', # fixme: use direct connections or ssh tunnel?
     permissions => {
+       description => "You also need to pass a valid ticket (vncticket).",
        check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
     },
-    description => "Returns a SPICE configuration to connect to the VM.",
+    description => "Opens a weksocket for VNC 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,
+           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 => {
-       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 {
@@ -1365,49 +1417,64 @@ __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 $authpath = "/vms/$vmid";
 
-       my $timeout = 10;
+       PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
 
-       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 $conf = PVE::QemuServer::load_config($vmid, $node); # VM exists ?
 
-       if (!$proxy) {
-           my $host = `hostname -f` || PVE::INotify::nodename();
-           chomp $host;
-           $proxy = $host;
-       }
+       # 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',
+    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) = @_;
 
-       # read x509 subject
-       my $filename = "/etc/pve/local/pve-ssl.pem";
-       my $bio = Net::SSLeay::BIO_new_file($filename, 'r');
-       my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
-       Net::SSLeay::BIO_free($bio);
-       my $nameobj =  Net::SSLeay::X509_get_subject_name($x509);
-       my $subject = Net::SSLeay::X509_NAME_oneline($nameobj);
-       Net::SSLeay::X509_free($x509);
+       my $rpcenv = PVE::RPCEnvironment::get();
+
+       my $authuser = $rpcenv->get_user();
+
+       my $vmid = $param->{vmid};
+       my $node = $param->{node};
+       my $proxy = $param->{proxy};
 
-       # remote-viewer wants comma as seperator (not '/')
-       $subject =~ s!^/!!;
-       $subject =~ s!/(\w+=)!,$1!g;
+       my $conf = PVE::QemuServer::load_config($vmid, $node);
+       my $title = "VM $vmid - $conf->{'name'}",
 
-       my $cacert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192);
-       $cacert =~ s/\n/\\n/g;
+       my $port = PVE::QemuServer::spice_port($vmid);
 
-       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 ($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({
@@ -1490,9 +1557,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;
     }});
@@ -1546,6 +1611,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 &&
@@ -1574,7 +1648,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;
            };
@@ -1599,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',
@@ -1643,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;
@@ -2053,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', {
@@ -2181,14 +2255,20 @@ __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 {
-                       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};
@@ -2199,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}) {
@@ -2243,6 +2331,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);
@@ -2392,6 +2483,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 = $@) {
 
@@ -2403,8 +2501,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 $@;
+                   }
                }
            };
 
@@ -2629,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']);