]> git.proxmox.com Git - qemu-server.git/commitdiff
fix #3010: add 'bootorder' parameter for better control of boot devices
authorStefan Reiter <s.reiter@proxmox.com>
Tue, 6 Oct 2020 13:32:15 +0000 (15:32 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 14 Oct 2020 10:30:50 +0000 (12:30 +0200)
(also fixes #3011)

Deprecates the old-style 'boot' and 'bootdisk' options by adding a new
'order=' subproperty to 'boot'.

This allows a user to specify more than one disk in the boot order,
helping with newer versions of SeaBIOS/OVMF where disks without a
bootindex won't be initialized at all (breaks soft-raid and some LVM
setups).

This also allows specifying a bootindex for USB and hostpci devices,
which was not possible before. Floppy boot support is not supported in
the new model, but I doubt that will be a problem (AFAICT we can't even
attach floppy disks to a VM?).

Default behaviour is intended to stay the same, i.e. while new VMs will
receive the new 'order' property, it will be set so the VM starts the
same as before (using get_default_bootorder).

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
PVE/API2/Qemu.pm
PVE/CLI/qm.pm
PVE/QemuServer.pm
PVE/QemuServer/Drive.pm
PVE/QemuServer/PCI.pm
PVE/QemuServer/USB.pm

index 8da616a503a792264c3858bc52e2cce843407bf3..0d82d3e459f2e0a7e64fa9403c94a1154adfe6fe 100644 (file)
@@ -656,9 +656,9 @@ __PACKAGE__->register_method({
                eval {
                    $vollist = &$create_disks($rpcenv, $authuser, $conf, $arch, $storecfg, $vmid, $pool, $param, $storage);
 
-                   if (!$conf->{bootdisk}) {
-                       my $firstdisk = PVE::QemuServer::Drive::resolve_first_disk($conf);
-                       $conf->{bootdisk} = $firstdisk if $firstdisk;
+                   if (!$conf->{boot}) {
+                       my $devs = PVE::QemuServer::get_default_bootdevices($conf);
+                       $conf->{boot} = PVE::QemuServer::print_bootorder($devs);
                    }
 
                    # auto generate uuid if user did not specify smbios1 option
index 282fa862c77585e133d2ce3f818b1d294ba11ba0..6243b0602d68c781d5960c5e4f06930674a63f56 100755 (executable)
@@ -656,8 +656,8 @@ __PACKAGE__->register_method ({
 
            # reload after disks entries have been created
            $conf = PVE::QemuConfig->load_config($vmid);
-           my $firstdisk = PVE::QemuServer::Drive::resolve_first_disk($conf);
-           $conf->{bootdisk} = $firstdisk if $firstdisk;
+           my $devs = PVE::QemuServer::get_default_bootdevices($conf);
+           $conf->{boot} = PVE::QemuServer::print_bootorder($devs);
            PVE::QemuConfig->write_config($vmid, $conf);
        };
 
index cfac03a92858236763b8f9637efd04af2f94f8b8..827957198611b77eac7a5c11e21d405886812416 100644 (file)
@@ -397,15 +397,14 @@ EODESC
     },
     boot => {
        optional => 1,
-       type => 'string',
-       description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
-       pattern => '[acdn]{1,4}',
-       default => 'cdn',
+       type => 'string', format => 'pve-qm-boot',
+       description => "Specify guest boot order. Use with 'order=', usage with"
+                    . " no key or 'legacy=' is deprecated.",
     },
     bootdisk => {
        optional => 1,
        type => 'string', format => 'pve-qm-bootdisk',
-       description => "Enable booting from specified disk.",
+       description => "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
        pattern => '(ide|sata|scsi|virtio)\d+',
     },
     smp => {
@@ -1614,8 +1613,6 @@ sub print_drive_commandline_full {
 sub print_netdevice_full {
     my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
 
-    my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
-
     my $device = $net->{model};
     if ($net->{model} eq 'virtio') {
          $device = 'virtio-net-pci';
@@ -3213,17 +3210,30 @@ sub config_to_command {
        push @$devices, '-device', $kbd if defined($kbd);
     }
 
+    my $bootorder = {};
+    my $boot = parse_property_string($boot_fmt, $conf->{boot}) if $conf->{boot};
+    if (!defined($boot) || $boot->{legacy}) {
+       $bootorder = bootorder_from_legacy($conf, $boot);
+    } elsif ($boot->{order}) {
+       # start at 100 to allow user to insert devices before us with -args
+       my $i = 100;
+       for my $dev (PVE::Tools::split_list($boot->{order})) {
+           $bootorder->{$dev} = $i++;
+       }
+    }
+
     # host pci device passthrough
     my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE::QemuServer::PCI::print_hostpci_devices(
-       $vmid, $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type);
+       $vmid, $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
 
     # usb devices
     my $usb_dev_features = {};
     $usb_dev_features->{spice_usb3} = 1 if min_version($machine_version, 4, 0);
 
     my @usbdevices = PVE::QemuServer::USB::get_usb_devices(
-        $conf, $usbdesc->{format}, $MAX_USB_DEVICES, $usb_dev_features);
+        $conf, $usbdesc->{format}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder);
     push @$devices, @usbdevices if @usbdevices;
+
     # serial devices
     for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
        if (my $path = $conf->{"serial$i"}) {
@@ -3291,15 +3301,6 @@ sub config_to_command {
     }
     push @$cmd, '-nodefaults';
 
-    my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
-
-    my $bootindex_hash = {};
-    my $i = 1;
-    foreach my $o (split(//, $bootorder)) {
-       $bootindex_hash->{$o} = $i*100;
-       $i++;
-    }
-
     push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
 
     push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
@@ -3469,17 +3470,7 @@ sub config_to_command {
 
        $use_virtio = 1 if $ds =~ m/^virtio/;
 
-       if (drive_is_cdrom ($drive)) {
-           if ($bootindex_hash->{d}) {
-               $drive->{bootindex} = $bootindex_hash->{d};
-               $bootindex_hash->{d} += 1;
-           }
-       } else {
-           if ($bootindex_hash->{c}) {
-               $drive->{bootindex} = $bootindex_hash->{c} if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds);
-               $bootindex_hash->{c} += 1;
-           }
-       }
+       $drive->{bootindex} = $bootorder->{$ds} if $bootorder->{$ds};
 
        if ($drive->{interface} eq 'virtio'){
            push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread};
@@ -3530,22 +3521,21 @@ sub config_to_command {
     });
 
     for (my $i = 0; $i < $MAX_NETS; $i++) {
-       next if !$conf->{"net$i"};
-       my $d = parse_net($conf->{"net$i"});
+       my $netname = "net$i";
+
+       next if !$conf->{$netname};
+       my $d = parse_net($conf->{$netname});
        next if !$d;
 
        $use_virtio = 1 if $d->{model} eq 'virtio';
 
-       if ($bootindex_hash->{n}) {
-           $d->{bootindex} = $bootindex_hash->{n};
-           $bootindex_hash->{n} += 1;
-       }
+       $d->{bootindex} = $bootorder->{$netname} if $bootorder->{$netname};
 
-       my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, "net$i");
+       my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, $netname);
        push @$devices, '-netdev', $netdevfull;
 
        my $netdevicefull = print_netdevice_full(
-           $vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
+           $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type);
 
        push @$devices, '-device', $netdevicefull;
     }
@@ -3827,7 +3817,8 @@ sub vm_deviceunplug {
     my $devices_list = vm_devices_list($vmid);
     return 1 if !defined($devices_list->{$deviceid});
 
-    die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
+    my $bootdisks = PVE::QemuServer::Drive::get_bootdisks($conf);
+    die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
 
     if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
 
index b71fc9336ef1c6f575c8d78d2e289ba2c05b490d..6f69a4ba87b5591f491e24a7448f1e9818074572 100644 (file)
@@ -501,11 +501,28 @@ sub print_drive {
     return PVE::JSONSchema::print_property_string($drive, $alldrive_fmt, $skip);
 }
 
+sub get_bootdisks {
+    my ($conf) = @_;
+
+    my $bootcfg = PVE::JSONSchema::parse_property_string('pve-qm-boot', $conf->{boot})
+       if $conf->{boot};
+
+    if (!defined($bootcfg) || $bootcfg->{legacy}) {
+       return [$conf->{bootdisk}] if $conf->{bootdisk};
+       return [];
+    }
+
+    my @list = PVE::Tools::split_list($bootcfg->{order});
+    @list = grep {is_valid_drivename($_)} @list;
+    return \@list;
+}
+
 sub bootdisk_size {
     my ($storecfg, $conf) = @_;
 
-    my $bootdisk = $conf->{bootdisk};
-    return undef if !$bootdisk;
+    my $bootdisks = get_bootdisks($conf);
+    return undef if !@$bootdisks;
+    my $bootdisk = $bootdisks->[0];
     return undef if !is_valid_drivename($bootdisk);
 
     return undef if !$conf->{$bootdisk};
index cb368458c21fbce0e89ec2e7bd88303db8ba9285..2df27085eeb8b2171f4213a65fb536319f795ffb 100644 (file)
@@ -357,7 +357,7 @@ sub parse_hostpci {
 }
 
 sub print_hostpci_devices {
-    my ($vmid, $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type) = @_;
+    my ($vmid, $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder) = @_;
 
     my $kvm_off = 0;
     my $gpu_passthrough = 0;
@@ -446,6 +446,7 @@ sub print_hostpci_devices {
                $devicestr .= "$xvga";
                $devicestr .= ",multifunction=on" if $multifunction;
                $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
+               $devicestr .= ",bootindex=$bootorder->{$id}" if $bootorder->{$id};
            }
 
            push @$devices, '-device', $devicestr;
index d328148539fc9245a2ac58fb442e5e307578b7bd..4a843cd4c10b888eb7062695fa58da7a528f85b6 100644 (file)
@@ -74,13 +74,14 @@ sub get_usb_controllers {
 }
 
 sub get_usb_devices {
-    my ($conf, $format, $max_usb_devices, $features) = @_;
+    my ($conf, $format, $max_usb_devices, $features, $bootorder) = @_;
 
     my $devices = [];
 
     for (my $i = 0; $i < $max_usb_devices; $i++)  {
-       next if !$conf->{"usb$i"};
-       my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) };
+       my $devname = "usb$i";
+       next if !$conf->{$devname};
+       my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{$devname}) };
        next if !$d;
 
        if (defined($d->{host})) {
@@ -93,8 +94,10 @@ sub get_usb_devices {
 
                push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
                push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=$bus.0";
+
+               warn "warning: spice usb port set as bootdevice, ignoring\n" if $bootorder->{$devname};
            } else {
-               push @$devices, '-device', print_usbdevice_full($conf, "usb$i", $hostdevice);
+               push @$devices, '-device', print_usbdevice_full($conf, $devname, $hostdevice, $bootorder);
            }
        }
     }
@@ -103,7 +106,7 @@ sub get_usb_devices {
 }
 
 sub print_usbdevice_full {
-    my ($conf, $deviceid, $device) = @_;
+    my ($conf, $deviceid, $device, $bootorder) = @_;
 
     return if !$device;
     my $usbdevice = "usb-host";
@@ -120,6 +123,7 @@ sub print_usbdevice_full {
     }
 
     $usbdevice .= ",id=$deviceid";
+    $usbdevice .= ",bootindex=$bootorder->{$deviceid}" if $bootorder->{$deviceid};
     return $usbdevice;
 }