]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
fix bug #121: activate volumes correctly
[qemu-server.git] / PVE / API2 / Qemu.pm
index b7ef2190ddc5ceae99b07b20f8295f7fc7f541fb..9537a3cb7f424bda1d14c700efbc1d07bb4ce890 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use Cwd 'abs_path';
 
-use PVE::Cluster;
+use PVE::Cluster qw (cfs_read_file cfs_write_file);;
 use PVE::SafeSyslog;
 use PVE::Tools qw(extract_param);
 use PVE::Exception qw(raise raise_param_exc);
@@ -33,30 +33,9 @@ my $resolve_cdrom_alias = sub {
     }
 };
 
-my $check_volume_access = sub {
-    my ($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool) = @_;
-
-    my $path;
-    if (my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1)) {
-       my ($ownervm, $vtype);
-       ($path, $ownervm, $vtype) = PVE::Storage::path($storecfg, $volid);
-       if ($vtype eq 'iso' || $vtype eq 'vztmpl') {
-           # we simply allow access 
-       } elsif (!$ownervm || ($ownervm != $vmid)) {
-           # allow if we are Datastore administrator
-           $rpcenv->check_storage_perm($authuser, $vmid, $pool, $sid, [ 'Datastore.Allocate' ]);
-       }
-    } else {
-       die "Only root can pass arbitrary filesystem paths."
-           if $authuser ne 'root@pam';
-
-       $path = abs_path($volid);
-    }
-    return $path;
-};
 
 my $check_storage_access = sub {
-   my ($rpcenv, $authuser, $storecfg, $vmid, $pool, $settings, $default_storage) = @_;
+   my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
 
    PVE::QemuServer::foreach_drive($settings, sub {
        my ($ds, $drive) = @_;
@@ -67,13 +46,14 @@ my $check_storage_access = sub {
 
        if (!$volid || $volid eq 'none') {
            # nothing to check
+       } elsif ($isCDROM && ($volid eq 'cdrom')) {
+           $rpcenv->check($authuser, "/", ['Sys.Console']);
        } elsif (!$isCDROM && ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/)) {
            my ($storeid, $size) = ($2 || $default_storage, $3);
            die "no storage ID specified (and no default storage)\n" if !$storeid;
-           $rpcenv->check_storage_perm($authuser, $vmid, $pool, $storeid, [ 'Datastore.AllocateSpace' ]);
+           $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
        } else {
-           my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
-           die "image '$path' does not exists\n" if (!(-f $path || -b $path));
+           $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
        }
     });
 };
@@ -91,7 +71,7 @@ my $create_disks = sub {
 
        my $volid = $disk->{file};
 
-       if (!$volid || $volid eq 'none') {
+       if (!$volid || $volid eq 'none' || $volid eq 'cdrom') {
            $res->{$ds} = $settings->{$ds};
        } elsif ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
            my ($storeid, $size) = ($2 || $default_storage, $3);
@@ -105,7 +85,8 @@ my $create_disks = sub {
            delete $disk->{format}; # no longer needed
            $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
        } else {
-           my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
+           my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
+           PVE::Storage::activate_volumes($storecfg, [ $volid ]);
            die "image '$path' does not exists\n" if (!(-f $path || -b $path));
            $res->{$ds} = $settings->{$ds};
        }
@@ -132,7 +113,7 @@ my $create_disks = sub {
 my $check_vm_modify_config_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
 
-    return 1 if $authuser ne 'root@pam';
+    return 1 if $authuser eq 'root@pam';
 
     foreach my $opt (@$key_list) {
        # disk checks need to be done somewhere else
@@ -140,7 +121,7 @@ my $check_vm_modify_config_perm = sub {
 
        if ($opt eq 'sockets' || $opt eq 'cores' ||
            $opt eq 'cpu' || $opt eq 'smp' || 
-           $opt eq 'cpuimit' || $opt eq 'cpuunits') {
+           $opt eq 'cpulimit' || $opt eq 'cpuunits') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
        } elsif ($opt eq 'boot' || $opt eq 'bootdisk') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
@@ -290,13 +271,13 @@ __PACKAGE__->register_method({
            $rpcenv->check_perm_modify($authuser, "/pool/$pool");
        } 
 
-       $rpcenv->check_storage_perm($authuser, $vmid, $pool, $storage, [ 'Datastore.AllocateSpace' ])
+       $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace'])
            if defined($storage);
 
        if (!$archive) {
            &$resolve_cdrom_alias($param);
 
-           &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $pool, $param, $storage);
+           &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
 
            &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
 
@@ -319,12 +300,22 @@ __PACKAGE__->register_method({
                die "pipe requires cli environment\n"
                    && $rpcenv->{type} ne 'cli';
            } else {
-               my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $archive, $pool);
+               my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
+               PVE::Storage::activate_volumes($storecfg, [ $archive ]);
                die "can't find archive file '$archive'\n" if !($path && -f $path);
                $archive = $path;
            }
        }
 
+       my $addVMtoPoolFn = sub {                      
+           my $usercfg = cfs_read_file("user.cfg");
+           if (my $data = $usercfg->{pools}->{$pool}) {
+               $data->{vms}->{$vmid} = 1;
+               $usercfg->{vms}->{$vmid} = $pool;
+               cfs_write_file("user.cfg", $usercfg);
+           }
+       };
+
        my $restorefn = sub {
 
            if (-f $filename) {
@@ -343,6 +334,8 @@ __PACKAGE__->register_method({
                    storage => $storage,
                    pool => $pool,
                    unique => $unique });
+
+               PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
            };
 
            return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd);
@@ -350,7 +343,7 @@ __PACKAGE__->register_method({
 
        my $createfn = sub {
 
-           # second test (after locking test is accurate)
+           # test after locking
            die "unable to create vm $vmid: config file already exists\n"
                if -f $filename;
 
@@ -378,7 +371,8 @@ __PACKAGE__->register_method({
                        $conf->{bootdisk} = $firstdisk;
                    }
 
-                   PVE::QemuServer::update_conf_nolock($vmid, $conf);
+                   PVE::QemuServer::update_config_nolock($vmid, $conf);
+
                };
                my $err = $@;
 
@@ -389,12 +383,14 @@ __PACKAGE__->register_method({
                    }
                    die "create failed - $err";
                }
+
+               PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
            };
 
            return $rpcenv->fork_worker('qmcreate', $vmid, $authuser, $realcmd);
        };
 
-       return PVE::QemuServer::lock_config($vmid, $archive ? $restorefn : $createfn);
+       return PVE::QemuServer::lock_config_full($vmid, 1, $archive ? $restorefn : $createfn);
     }});
 
 __PACKAGE__->register_method({
@@ -603,9 +599,10 @@ my $delete_drive = sub {
            } else {
                PVE::QemuServer::add_unused_volume($conf, $volid, $vmid);
            }
-           delete $conf->{$key};
        }
     }
+
+    delete $conf->{$key};
 };
 
 my $vmconfig_delete_option = sub {
@@ -620,7 +617,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_storage_perm($authuser, $vmid, undef, $sid, [ 'Datastore.Allocate' ]);
+           $rpcenv->check($authuser, "/storage/$sid", ['Datastore.Allocate']);
        }
     }
                
@@ -817,7 +814,7 @@ __PACKAGE__->register_method({
 
        &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
 
-       &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, undef, $param);
+       &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
 
        my $updatefn =  sub {
 
@@ -904,12 +901,24 @@ __PACKAGE__->register_method({
 
        my $storecfg = PVE::Storage::config();
 
+       my $delVMfromPoolFn = sub {                    
+           my $usercfg = cfs_read_file("user.cfg");
+           my $pool = $usercfg->{vms}->{$vmid};
+           if (my $data = $usercfg->{pools}->{$pool}) {
+               delete $data->{vms}->{$vmid};
+               delete $usercfg->{vms}->{$vmid};
+               cfs_write_file("user.cfg", $usercfg);
+           }
+       };
+
        my $realcmd = sub {
            my $upid = shift;
 
            syslog('info', "destroy VM $vmid: $upid\n");
 
            PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock);
+
+           PVE::AccessControl::lock_user_config($delVMfromPoolFn, "pool cleanup failed");
        };
 
        return $rpcenv->fork_worker('qmdestroy', $vmid, $authuser, $realcmd);
@@ -1081,6 +1090,16 @@ __PACKAGE__->register_method({
        return $res;
     }});
 
+my $vm_is_ha_managed = sub {
+    my ($vmid) = @_;
+
+    my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
+    if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1)) {
+       return 1;
+    } 
+    return 0;
+};
+
 __PACKAGE__->register_method({
     name => 'vm_status',
     path => '{vmid}/status/current',
@@ -1108,12 +1127,7 @@ __PACKAGE__->register_method({
        my $vmstatus = PVE::QemuServer::vmstatus($param->{vmid});
        my $status = $vmstatus->{$param->{vmid}};
 
-       my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
-       if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $param->{vmid}, 1)) {
-           $status->{ha} = 1;
-       } else {
-           $status->{ha} = 0;
-       }
+       $status->{ha} = &$vm_is_ha_managed($param->{vmid});
 
        return $status;
     }});
@@ -1161,17 +1175,39 @@ __PACKAGE__->register_method({
 
        my $storecfg = PVE::Storage::config();
 
-       my $realcmd = sub {
-           my $upid = shift;
+       if (&$vm_is_ha_managed($vmid) && !$stateuri &&
+           $rpcenv->{type} ne 'ha') {
 
-           syslog('info', "start VM $vmid: $upid\n");
+           my $hacmd = sub {
+               my $upid = shift;
 
-           PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock);
+               my $service = "pvevm:$vmid";
 
-           return;
-       };
+               my $cmd = ['clusvcadm', '-e', $service, '-m', $node];
+
+               print "Executing HA start for VM $vmid\n";
+
+               PVE::Tools::run_command($cmd);
+
+               return;
+           };
+
+           return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
+
+       } else {
+
+           my $realcmd = sub {
+               my $upid = shift;
+
+               syslog('info', "start VM $vmid: $upid\n");
+
+               PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock);
 
-       return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
+               return;
+           };
+
+           return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
+       }
     }});
 
 __PACKAGE__->register_method({
@@ -1228,18 +1264,38 @@ __PACKAGE__->register_method({
 
        my $storecfg = PVE::Storage::config();
 
-       my $realcmd = sub {
-           my $upid = shift;
+       if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
 
-           syslog('info', "stop VM $vmid: $upid\n");
+           my $hacmd = sub {
+               my $upid = shift;
 
-           PVE::QemuServer::vm_stop($storecfg, $vmid, $skiplock, 0,
-                                    $param->{timeout}, 0, 1, $keepActive);
+               my $service = "pvevm:$vmid";
 
-           return;
-       };
+               my $cmd = ['clusvcadm', '-d', $service];
+
+               print "Executing HA stop for VM $vmid\n";
+
+               PVE::Tools::run_command($cmd);
+
+               return;
+           };
+
+           return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
+
+       } else {
+           my $realcmd = sub {
+               my $upid = shift;
+
+               syslog('info', "stop VM $vmid: $upid\n");
+
+               PVE::QemuServer::vm_stop($storecfg, $vmid, $skiplock, 0,
+                                        $param->{timeout}, 0, 1, $keepActive);
+
+               return;
+           };
 
-       return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
+           return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
+       }
     }});
 
 __PACKAGE__->register_method({
@@ -1577,15 +1633,35 @@ __PACKAGE__->register_method({
                if !$param->{online};
        }
 
-       my $realcmd = sub {
-           my $upid = shift;
+       if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
 
-           PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
-       };
+           my $hacmd = sub {
+               my $upid = shift;
+
+               my $service = "pvevm:$vmid";
+
+               my $cmd = ['clusvcadm', '-M', $service, '-m', $target];
+
+               print "Executing HA migrate for VM $vmid to node $target\n";
+
+               PVE::Tools::run_command($cmd);
+
+               return;
+           };
+
+           return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
 
-       my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
+       } else {
+
+           my $realcmd = sub {
+               my $upid = shift;
+
+               PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
+           };
+
+           return $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
+       }
 
-       return $upid;
     }});
 
 __PACKAGE__->register_method({