]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
api: create disks: avoid adding secondary cloud-init drives
[qemu-server.git] / PVE / API2 / Qemu.pm
index 3af21325796399dbf189d550984b3c68ce6b4043..3ec31c26898644114c7727d9030a6545626dd849 100644 (file)
@@ -142,14 +142,12 @@ my $check_storage_access = sub {
            raise_param_exc({ storage => "storage '$storeid' does not support vm images"})
                if !$scfg->{content}->{images};
        } else {
-           PVE::Storage::check_volume_access(
-               $rpcenv,
-               $authuser,
-               $storecfg,
-               $vmid,
-               $volid,
-               'images',
-           );
+           PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid);
+           if ($storeid) {
+               my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
+               raise_param_exc({ $ds => "content type needs to be 'images' or 'iso'" })
+                   if $vtype ne 'images' && $vtype ne 'iso';
+           }
        }
 
        if (my $src_image = $drive->{'import-from'}) {
@@ -330,6 +328,15 @@ my $create_disks = sub {
        } elsif (defined($volname) && $volname eq 'cloudinit') {
            $storeid = $storeid // $default_storage;
            die "no storage ID specified (and no default storage)\n" if !$storeid;
+
+           if (
+               my $ci_key = PVE::QemuConfig->has_cloudinit($conf, $ds)
+               || PVE::QemuConfig->has_cloudinit($conf->{pending} || {}, $ds)
+               || PVE::QemuConfig->has_cloudinit($res, $ds)
+           ) {
+               die "$ds - cloud-init drive is already attached at '$ci_key'\n";
+           }
+
            my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
            my $name = "vm-$vmid-cloudinit";
 
@@ -421,14 +428,22 @@ my $create_disks = sub {
 
            print "$ds: successfully created disk '$res->{$ds}'\n";
        } else {
-           PVE::Storage::check_volume_access(
-               $rpcenv,
-               $authuser,
-               $storecfg,
-               $vmid,
-               $volid,
-               'images',
-           );
+           PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid);
+           if ($storeid) {
+               my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
+               die "cannot use volume $volid - content type needs to be 'images' or 'iso'"
+                   if $vtype ne 'images' && $vtype ne 'iso';
+
+               if (PVE::QemuServer::Drive::drive_is_cloudinit($disk)) {
+                   if (
+                       my $ci_key = PVE::QemuConfig->has_cloudinit($conf, $ds)
+                       || PVE::QemuConfig->has_cloudinit($conf->{pending} || {}, $ds)
+                       || PVE::QemuConfig->has_cloudinit($res, $ds)
+                   ) {
+                       die "$ds - cloud-init drive is already attached at '$ci_key'\n";
+                   }
+               }
+           }
 
            PVE::Storage::activate_volumes($storecfg, [ $volid ]) if $storeid;
 
@@ -822,24 +837,12 @@ __PACKAGE__->register_method({
            raise_perm_exc();
        }
 
-       if (!$archive) {
-           &$resolve_cdrom_alias($param);
-
-           &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
-
-           &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
-
-           &$check_vm_create_serial_perm($rpcenv, $authuser, $vmid, $pool, $param);
-           &$check_vm_create_usb_perm($rpcenv, $authuser, $vmid, $pool, $param);
-
-           &$check_cpu_model_access($rpcenv, $authuser, $param);
-
-           $check_drive_param->($param, $storecfg);
-
-           PVE::QemuServer::add_random_macs($param);
-       } else {
-           my $keystr = join(' ', keys %$param);
-           raise_param_exc({ archive => "option conflicts with other options ($keystr)"}) if $keystr;
+       if ($archive) {
+           for my $opt (sort keys $param->%*) {
+               if (PVE::QemuServer::Drive::is_valid_drivename($opt)) {
+                   raise_param_exc({ $opt => "option conflicts with option 'archive'" });
+               }
+           }
 
            if ($archive eq '-') {
                die "pipe requires cli environment\n" if $rpcenv->{type} ne 'cli';
@@ -858,6 +861,23 @@ __PACKAGE__->register_method({
            }
        }
 
+       if (scalar(keys $param->%*) > 0) {
+           &$resolve_cdrom_alias($param);
+
+           &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
+
+           &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
+
+           &$check_vm_create_serial_perm($rpcenv, $authuser, $vmid, $pool, $param);
+           &$check_vm_create_usb_perm($rpcenv, $authuser, $vmid, $pool, $param);
+
+           &$check_cpu_model_access($rpcenv, $authuser, $param);
+
+           $check_drive_param->($param, $storecfg);
+
+           PVE::QemuServer::add_random_macs($param);
+       }
+
        my $emsg = $is_restore ? "unable to restore VM $vmid -" : "unable to create VM $vmid -";
 
        eval { PVE::QemuConfig->create_and_lock_config($vmid, $force) };
@@ -878,6 +898,7 @@ __PACKAGE__->register_method({
                    unique => $unique,
                    bwlimit => $bwlimit,
                    live => $live_restore,
+                   override_conf => $param,
                };
                if ($archive->{type} eq 'file' || $archive->{type} eq 'pipe') {
                    die "live-restore is only compatible with backup images from a Proxmox Backup Server\n"
@@ -2439,7 +2460,12 @@ __PACKAGE__->register_method({
 
        $status->{ha} = PVE::HA::Config::get_service_status("vm:$param->{vmid}");
 
-       $status->{spice} = 1 if PVE::QemuServer::vga_conf_has_spice($conf->{vga});
+       if ($conf->{vga}) {
+           my $vga = PVE::QemuServer::parse_vga($conf->{vga});
+           my $spice = defined($vga->{type}) && $vga->{type} =~ /^virtio/;
+           $spice ||= PVE::QemuServer::vga_conf_has_spice($conf->{vga});
+           $status->{spice} = 1 if $spice;
+       }
        $status->{agent} = 1 if PVE::QemuServer::get_qga_key($conf, 'enabled');
 
        return $status;
@@ -2536,7 +2562,7 @@ __PACKAGE__->register_method({
        my $spice_ticket;
        my $nbd_protocol_version = 0;
        my $replicated_volumes = {};
-       my $tpmstate_vol;
+       my $offline_volumes = {};
        if ($stateuri && ($stateuri eq 'tcp' || $stateuri eq 'unix') && $migratedfrom && ($rpcenv->{type} eq 'cli')) {
            while (defined(my $line = <STDIN>)) {
                chomp $line;
@@ -2546,8 +2572,10 @@ __PACKAGE__->register_method({
                    $nbd_protocol_version = $1;
                } elsif ($line =~ m/^replicated_volume: (.*)$/) {
                    $replicated_volumes->{$1} = 1;
-               } elsif ($line =~ m/^tpmstate0: (.*)$/) {
-                   $tpmstate_vol = $1;
+               } elsif ($line =~ m/^tpmstate0: (.*)$/) { # Deprecated, use offline_volume instead
+                   $offline_volumes->{tpmstate0} = $1;
+               } elsif ($line =~ m/^offline_volume: ([^:]+): (.*)$/) {
+                   $offline_volumes->{$1} = $2;
                } elsif (!$spice_ticket) {
                    # fallback for old source node
                    $spice_ticket = $line;
@@ -2589,7 +2617,7 @@ __PACKAGE__->register_method({
                    storagemap => $storagemap,
                    nbd_proto_version => $nbd_protocol_version,
                    replicated_volumes => $replicated_volumes,
-                   tpmstate_vol => $tpmstate_vol,
+                   offline_volumes => $offline_volumes,
                };
 
                my $params = {
@@ -2968,10 +2996,17 @@ __PACKAGE__->register_method({
        # early check for storage permission, for better user feedback
        if ($todisk) {
            $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
+           my $conf = PVE::QemuConfig->load_config($vmid);
+
+           # cannot save the state of a non-virtualized PCIe device, so resume cannot really work
+           for my $key (keys %$conf) {
+               next if $key !~ /^hostpci\d+/;
+               die "cannot suspend VM to disk due to passed-through PCI device(s), which lack the"
+                   ." possibility to save/restore their internal state\n";
+           }
 
            if (!$statestorage) {
                # get statestorage from config if none is given
-               my $conf = PVE::QemuConfig->load_config($vmid);
                my $storecfg = PVE::Storage::config();
                $statestorage = PVE::QemuServer::find_vmstate_storage($conf, $storecfg);
            }
@@ -3871,6 +3906,13 @@ __PACKAGE__->register_method({
 
                    $drive->{file} = $new_volid;
 
+                   my $boot_order = PVE::QemuServer::device_bootorder($source_conf);
+                   if (defined(delete $boot_order->{$disk})) {
+                       print "removing disk '$disk' from boot order config\n";
+                       my $boot_devs = [ sort { $boot_order->{$a} <=> $boot_order->{$b} } keys %$boot_order ];
+                       $source_conf->{boot} = PVE::QemuServer::print_bootorder($boot_devs);
+                   }
+
                    delete $source_conf->{$disk};
                    print "removing disk '${disk}' from VM '${vmid}' config\n";
                    PVE::QemuConfig->write_config($vmid, $source_conf);