]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
api/destroy: repeat early checks after lock
[qemu-server.git] / PVE / API2 / Qemu.pm
index 396879a5555542b61fb2e2d265f6c7bd6459cb91..867e245ab9a649922d195141a5009acb9034b26b 100644 (file)
@@ -63,7 +63,7 @@ my $NEW_DISK_RE = qr!^(([^/:\s]+):)?(\d+(\.\d+)?)$!;
 my $check_storage_access = sub {
    my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
 
-   PVE::QemuServer::foreach_drive($settings, sub {
+   PVE::QemuConfig->foreach_volume($settings, sub {
        my ($ds, $drive) = @_;
 
        my $isCDROM = PVE::QemuServer::drive_is_cdrom($drive);
@@ -96,7 +96,7 @@ my $check_storage_access_clone = sub {
 
    my $sharedvm = 1;
 
-   PVE::QemuServer::foreach_drive($conf, sub {
+   PVE::QemuConfig->foreach_volume($conf, sub {
        my ($ds, $drive) = @_;
 
        my $isCDROM = PVE::QemuServer::drive_is_cdrom($drive);
@@ -216,7 +216,7 @@ my $create_disks = sub {
        }
     };
 
-    eval { PVE::QemuServer::foreach_drive($settings, $code); };
+    eval { PVE::QemuConfig->foreach_volume($settings, $code); };
 
     # free allocated images on error
     if (my $err = $@) {
@@ -404,7 +404,7 @@ __PACKAGE__->register_method({
 my $parse_restore_archive = sub {
     my ($storecfg, $archive) = @_;
 
-    my ($archive_storeid, $archive_volname) = PVE::Storage::parse_volume_id($archive);
+    my ($archive_storeid, $archive_volname) = PVE::Storage::parse_volume_id($archive, 1);
 
     if (defined($archive_storeid)) {
        my $scfg =  PVE::Storage::storage_config($storecfg, $archive_storeid);
@@ -1129,6 +1129,7 @@ my $update_vm_api  = sub {
                push @delete, 'lock'; # this is the real deal to write it out
            }
            push @delete, 'runningmachine' if $conf->{runningmachine};
+           push @delete, 'runningcpu' if $conf->{runningcpu};
        }
 
        PVE::QemuConfig->check_lock($conf) if !$skiplock;
@@ -1467,32 +1468,38 @@ __PACKAGE__->register_method({
        raise_param_exc({ skiplock => "Only root may use this option." })
            if $skiplock && $authuser ne 'root@pam';
 
-       # test if VM exists
-       my $conf = PVE::QemuConfig->load_config($vmid);
-       my $storecfg = PVE::Storage::config();
-       PVE::QemuConfig->check_protection($conf, "can't remove VM $vmid");
+       my $early_checks = sub {
+           # test if VM exists
+           my $conf = PVE::QemuConfig->load_config($vmid);
+           PVE::QemuConfig->check_protection($conf, "can't remove VM $vmid");
 
-       my $ha_managed = PVE::HA::Config::service_is_configured("vm:$vmid");
+           my $ha_managed = PVE::HA::Config::service_is_configured("vm:$vmid");
 
-       if (!$param->{purge}) {
-           die "unable to remove VM $vmid - used in HA resources and purge parameter not set.\n"
-               if $ha_managed;
-           # don't allow destroy if with replication jobs but no purge param
-           my $repl_conf = PVE::ReplicationConfig->new();
-           $repl_conf->check_for_existing_jobs($vmid);
-       }
+           if (!$param->{purge}) {
+               die "unable to remove VM $vmid - used in HA resources and purge parameter not set.\n"
+                   if $ha_managed;
+               # don't allow destroy if with replication jobs but no purge param
+               my $repl_conf = PVE::ReplicationConfig->new();
+               $repl_conf->check_for_existing_jobs($vmid);
+           }
+
+           die "VM $vmid is running - destroy failed\n"
+               if PVE::QemuServer::check_running($vmid);
+
+           return $ha_managed;
+       };
 
-       # early tests (repeat after locking)
-       die "VM $vmid is running - destroy failed\n"
-           if PVE::QemuServer::check_running($vmid);
+       $early_checks->();
 
        my $realcmd = sub {
            my $upid = shift;
 
+           my $storecfg = PVE::Storage::config();
+
            syslog('info', "destroy VM $vmid: $upid\n");
            PVE::QemuConfig->lock_config($vmid, sub {
-               die "VM $vmid is running - destroy failed\n"
-                   if (PVE::QemuServer::check_running($vmid));
+               # repeat, config might have changed
+               my $ha_managed = $early_checks->();
 
                PVE::QemuServer::destroy_vm($storecfg, $vmid, $skiplock, { lock => 'destroyed' });
 
@@ -2024,11 +2031,12 @@ __PACKAGE__->register_method({
                optional => 1,
            },
            machine => get_standard_option('pve-qemu-machine'),
-           targetstorage => {
-               description => "Target storage for the migration. (Can be '1' to use the same storage id as on the source node.)",
+           'force-cpu' => {
+               description => "Override QEMU's -cpu argument with the given string.",
                type => 'string',
-               optional => 1
+               optional => 1,
            },
+           targetstorage => get_standard_option('pve-targetstorage'),
            timeout => {
                description => "Wait maximal timeout seconds.",
                type => 'integer',
@@ -2052,6 +2060,7 @@ __PACKAGE__->register_method({
        my $timeout = extract_param($param, 'timeout');
 
        my $machine = extract_param($param, 'machine');
+       my $force_cpu = extract_param($param, 'force-cpu');
 
        my $get_root_param = sub {
            my $value = extract_param($param, $_[0]);
@@ -2067,8 +2076,15 @@ __PACKAGE__->register_method({
        my $migration_network = $get_root_param->('migration_network');
        my $targetstorage = $get_root_param->('targetstorage');
 
-       raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." })
-           if $targetstorage && !$migratedfrom;
+       my $storagemap;
+
+       if ($targetstorage) {
+           raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." })
+               if !$migratedfrom;
+           $storagemap = eval { PVE::JSONSchema::parse_idmap($targetstorage, 'pve-storage-id') };
+           raise_param_exc({ targetstorage => "failed to parse storage map: $@" })
+               if $@;
+       }
 
        # read spice ticket from STDIN
        my $spice_ticket;
@@ -2114,9 +2130,25 @@ __PACKAGE__->register_method({
 
                syslog('info', "start VM $vmid: $upid\n");
 
-               PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock, $migratedfrom, undef, $machine,
-                                         $spice_ticket, $migration_network, $migration_type, $targetstorage, $timeout,
-                                         $nbd_protocol_version, $replicated_volumes);
+               my $migrate_opts = {
+                   migratedfrom => $migratedfrom,
+                   spice_ticket => $spice_ticket,
+                   network => $migration_network,
+                   type => $migration_type,
+                   storagemap => $storagemap,
+                   nbd_proto_version => $nbd_protocol_version,
+                   replicated_volumes => $replicated_volumes,
+               };
+
+               my $params = {
+                   statefile => $stateuri,
+                   skiplock => $skiplock,
+                   forcemachine => $machine,
+                   timeout => $timeout,
+                   forcecpu => $force_cpu,
+               };
+
+               PVE::QemuServer::vm_start($storecfg, $vmid, $params, $migrate_opts);
                return;
            };
 
@@ -2563,6 +2595,8 @@ __PACKAGE__->register_method({
            if $skiplock && $authuser ne 'root@pam';
 
        my $nocheck = extract_param($param, 'nocheck');
+       raise_param_exc({ nocheck => "Only root may use this option." })
+           if $nocheck && $authuser ne 'root@pam';
 
        my $to_disk_suspended;
        eval {
@@ -2584,7 +2618,7 @@ __PACKAGE__->register_method({
                PVE::QemuServer::vm_resume($vmid, $skiplock, $nocheck);
            } else {
                my $storecfg = PVE::Storage::config();
-               PVE::QemuServer::vm_start($storecfg, $vmid, undef, $skiplock);
+               PVE::QemuServer::vm_start($storecfg, $vmid, { skiplock => $skiplock });
            }
 
            return;
@@ -3370,9 +3404,7 @@ __PACKAGE__->register_method({
                description => "Enable live storage migration for local disk",
                optional => 1,
            },
-            targetstorage => get_standard_option('pve-storage-id', {
-               description => "Default target storage.",
-               optional => 1,
+            targetstorage => get_standard_option('pve-targetstorage', {
                completion => \&PVE::QemuServer::complete_migration_storage,
             }),
            bwlimit => {
@@ -3431,13 +3463,36 @@ __PACKAGE__->register_method({
            $param->{online} = 0;
        }
 
-       raise_param_exc({ targetstorage => "Live storage migration can only be done online." })
-           if !$param->{online} && $param->{targetstorage};
-
        my $storecfg = PVE::Storage::config();
 
-       if( $param->{targetstorage}) {
-           PVE::Storage::storage_check_node($storecfg, $param->{targetstorage}, $target);
+       if (my $targetstorage = $param->{targetstorage}) {
+           my $check_storage = sub {
+               my ($target_sid) = @_;
+               PVE::Storage::storage_check_node($storecfg, $target_sid, $target);
+               $rpcenv->check($authuser, "/storage/$target_sid", ['Datastore.AllocateSpace']);
+               my $scfg = PVE::Storage::storage_config($storecfg, $target_sid);
+               raise_param_exc({ targetstorage => "storage '$target_sid' does not support vm images"})
+                   if !$scfg->{content}->{images};
+           };
+
+           my $storagemap = eval { PVE::JSONSchema::parse_idmap($targetstorage, 'pve-storage-id') };
+           raise_param_exc({ targetstorage => "failed to parse storage map: $@" })
+               if $@;
+
+           $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk'])
+               if !defined($storagemap->{identity});
+
+           foreach my $source (values %{$storagemap->{entries}}) {
+               $check_storage->($source);
+           }
+
+           $check_storage->($storagemap->{default})
+               if $storagemap->{default};
+
+           PVE::QemuServer::check_storage_availability($storecfg, $conf, $target)
+               if $storagemap->{identity};
+
+           $param->{storagemap} = $storagemap;
         } else {
            PVE::QemuServer::check_storage_availability($storecfg, $conf, $target);
        }