]> git.proxmox.com Git - pve-installer.git/blobdiff - proxinstall
raise postifx main.cf compatibility_level to 2
[pve-installer.git] / proxinstall
index 9ac864b9fb78dbf52bb663f4dd1f3f902bfef388..6ee3aa0bad000365e132f4a8d0969eb31be452c3 100755 (executable)
@@ -13,11 +13,12 @@ use IO::File;
 use IO::Select;
 use Cwd 'abs_path';
 use Gtk3 '-init';
-use Gtk3::WebKit;
+use Gtk3::WebKit2;
 use Encode;
 use String::ShellQuote;
 use Data::Dumper;
 use File::Basename;
+use File::Path;
 use Time::HiRes;
 
 use ProxmoxInstallerSetup;
@@ -39,6 +40,11 @@ my $zfstestpool = "test_rpool";
 my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
 my $zfsrootvolname = "$setup->{product}-1";
 
+my $product_fullname = {
+    pve => 'Proxmox VE',
+    pmg => 'Proxmox MailGateway',
+};
+
 my $storage_cfg_zfs = <<__EOD__;
 dir: local
        path /var/lib/vz
@@ -96,9 +102,7 @@ my $proxmox_libdir = $opt_testmode ?
 my $proxmox_cddir = $opt_testmode ? "../pve-cd-builder/tmp/data-gz/" : "/cdrom";
 my $proxmox_pkgdir = "${proxmox_cddir}/proxmox/packages/";
 
-my $grub_plattform = "pc"; # pc, efi-amd64 or efi-ia32
-
-$grub_plattform = "efi-amd64" if -d "/sys/firmware/efi";
+my $boot_type = -d '/sys/firmware/efi' ? 'efi' : 'bios';
 
 my $IPV4OCTET = "(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])";
 my $IPV4RE = "(?:(?:$IPV4OCTET\\.){3}$IPV4OCTET)";
@@ -324,6 +328,8 @@ mynetworks = 127.0.0.0/8
 inet_interfaces = loopback-only
 recipient_delimiter = +
 
+compatibility_level = 2
+
 _EOD
 
 sub shellquote {
@@ -588,6 +594,7 @@ sub hd_list {
        next if $info !~ m/^E: DEVTYPE=disk$/m;
 
        next if $info =~ m/^E: ID_CDROM/m;
+       next if $info =~ m/^E: ID_FS_TYPE=iso9660/m;
 
        my ($name) = $info =~ m/^N: (\S+)$/m;
 
@@ -615,7 +622,7 @@ sub hd_list {
 
 sub read_cmap {
     my $countryfn = "${proxmox_libdir}/country.dat";
-    open (TMP, "<$countryfn") || die "unable to open '$countryfn' - $!\n";
+    open (TMP, "<:encoding(utf8)", "$countryfn") || die "unable to open '$countryfn' - $!\n";
     my $line;
     my $country = {};
     my $countryhash = {};
@@ -691,6 +698,9 @@ sub get_partition_dev {
 
     if ($dev =~ m|^/dev/sd([a-h]?[a-z]\|i[a-v])$|) {
        return "${dev}$partnum";
+    } elsif ($dev =~ m|^/dev/xvd[a-z]$|) {
+       # Citrix Hypervisor blockdev
+       return "${dev}$partnum";
     } elsif ($dev =~ m|^/dev/[hxev]d[a-z]$|) {
        return "${dev}$partnum";
     } elsif ($dev =~ m|^/dev/[^/]+/c\d+d\d+$|) {
@@ -898,6 +908,7 @@ my $clean_disk = sub {
        next if $part eq $disk;
        next if $part !~ /^\Q$disk\E/;
        eval { syscmd("pvremove -ff -y $part"); };
+       eval { syscmd("zpool labelclear -f $part"); };
        eval { syscmd("dd if=/dev/zero of=$part bs=1M count=16"); };
     }
 };
@@ -963,11 +974,78 @@ sub partition_bootable_disk {
     return ($os_size, $osdev, $efibootdev);
 }
 
+sub get_pv_list_from_vgname {
+    my ($vgname) = @_;
+
+    my $res;
+
+    my $parser = sub {
+       my $line = shift;
+       $line =~ s/^\s+//;
+       $line =~ s/\s+$//;
+       return if !$line;
+       my ($pv, $vg_uuid) = split(/\s+/, $line);
+
+       if (!defined($res->{$vg_uuid}->{pvs})) {
+           $res->{$vg_uuid}->{pvs} = "$pv";
+       } else {
+           $res->{$vg_uuid}->{pvs} .= ", $pv";
+       }
+    };
+    run_command("pvs --noheadings -o pv_name,vg_uuid -S vg_name='$vgname'", $parser, undef, 1);
+
+    return $res;
+}
+
+sub ask_existing_vg_rename_or_abort {
+    my ($vgname) = @_;
+
+    # this normally only happens if one put a disk with a PVE installation in
+    # this server and that disk is not the installation target.
+    my $duplicate_vgs = get_pv_list_from_vgname($vgname);
+    return if !$duplicate_vgs;
+
+    my $message = "Detected existing '$vgname' Volume Group(s)! Do you want to:\n";
+
+    for my $vg_uuid (keys %$duplicate_vgs) {
+       my $vg = $duplicate_vgs->{$vg_uuid};
+
+       # no high randomnes properties, but this is only for the cases where
+       # we either have multiple "$vgname" vgs from multiple old PVE disks, or
+       # we have a disk with both a "$vgname" and "$vgname-old"...
+       my $short_uid = sprintf "%08X", rand(0xffffffff);
+       $vg->{new_vgname} = "$vgname-OLD-$short_uid";
+
+       $message .= "rename VG backed by PV '$vg->{pvs}' to '$vg->{new_vgname}'\n";
+    }
+    $message .= "or cancel the installation?";
+
+    my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'ok-cancel', $message);
+    my $response = $dialog->run();
+    $dialog->destroy();
+
+    if ($response eq 'ok') {
+       for my $vg_uuid (keys %$duplicate_vgs) {
+           my $vg = $duplicate_vgs->{$vg_uuid};
+           my $new_vgname = $vg->{new_vgname};
+
+           syscmd("vgrename $vg_uuid $new_vgname") == 0 ||
+               die "could not rename VG from '$vg->{pvs}' ($vg_uuid) to '$new_vgname'!\n";
+       }
+    } else {
+       set_next("_Reboot", sub { exit (0); } );
+       display_html("fail.htm");
+       die "Cancled installation by user, due to already existing volume group '$vgname'\n";
+    }
+}
+
 sub create_lvm_volumes {
     my ($lvmdev, $os_size, $swap_size) = @_;
 
     my $vgname = $setup->{product};
 
+    ask_existing_vg_rename_or_abort($vgname);
+
     my $rootdev = "/dev/$vgname/root";
     my $datadev = "/dev/$vgname/data";
     my $swapfile;
@@ -1075,6 +1153,43 @@ sub compute_swapsize {
     return $swapsize;
 }
 
+sub prepare_systemd_boot_esp {
+    my ($espdev, $targetdir) = @_;
+
+    syscmd("chroot $targetdir pve-efiboot-tool init $espdev") == 0 ||
+       die "unable to init ESP and install systemd-boot loader on '$espdev'\n";
+}
+
+sub prepare_grub_efi_boot_esp {
+    my ($dev, $espdev, $targetdir) = @_;
+
+    syscmd("mount -n $espdev -t vfat $targetdir/boot/efi") == 0 ||
+       die "unable to mount $espdev\n";
+
+    eval {
+       my $rc = syscmd("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev");
+       if ($rc != 0) {
+           if ($boot_type eq 'efi') {
+               die "unable to install the EFI boot loader on '$dev'\n";
+           } else {
+               warn "unable to install the EFI boot loader on '$dev', ignoring (not booted using UEFI)\n";
+           }
+       }
+       # also install fallback boot file (OVMF does not boot without)
+       mkdir("$targetdir/boot/efi/EFI/BOOT");
+       syscmd("cp $targetdir/boot/efi/EFI/proxmox/grubx64.efi $targetdir/boot/efi/EFI/BOOT/BOOTx64.EFI") == 0 ||
+           die "unable to copy efi boot loader\n";
+    };
+    my $err = $@;
+
+    eval {
+       syscmd("umount $targetdir/boot/efi") == 0 ||
+           die "unable to umount $targetdir/boot/efi\n";
+    };
+    warn $@ if $@;
+
+    die "failed to prepare EFI boot using Grub on '$espdev': $err" if $err;
+}
 
 sub extract_data {
     my ($basefile, $targetdir) = @_;
@@ -1114,6 +1229,8 @@ sub extract_data {
        die "unable to load zfs kernel module\n" if !$i;
     }
 
+    my $bootloader_err;
+
     eval {
 
 
@@ -1175,17 +1292,24 @@ sub extract_data {
 
            my ($devlist, $bootdevlist, $vdev) = get_zfs_raid_setup();
 
-           my $disksize;
            foreach my $hd (@$devlist) {
                &$clean_disk(@$hd[1]);
            }
+
+           my $disksize;
            foreach my $hd (@$bootdevlist) {
                my $devname = @$hd[1];
 
-               my ($size, $osdev) =
+               my ($size, $osdev, $efidev) =
                    partition_bootable_disk($devname, $config_options->{hdsize}, 'BF01');
+
                zfs_mirror_size_check($disksize, $size) if $disksize;
-               push @$bootdevinfo, { devname => $devname, osdev => $osdev};
+
+               push @$bootdevinfo, {
+                   esp => $efidev,
+                   devname => $devname,
+                   osdev => $osdev
+               };
                $disksize = $size;
            }
 
@@ -1195,11 +1319,8 @@ sub extract_data {
                my $devname = $di->{devname};
                $di->{by_id} = find_stable_path ("/dev/disk/by-id", $devname);
 
-               # Note: using /dev/disk/by-id/ does not work for unknown reason, we get
-               # cannot create 'rpool': no such pool or dataset
-               #my $osdev = find_stable_path ("/dev/disk/by-id", $di->{osdev}) || $di->{osdev};
+               my $osdev = find_stable_path ("/dev/disk/by-id", $di->{osdev}) || $di->{osdev};
 
-               my $osdev = $di->{osdev};
                $vdev =~ s/ $devname/ $osdev/;
            }
 
@@ -1286,6 +1407,11 @@ sub extract_data {
            }
        }
 
+       mkdir "$targetdir/mnt";
+       mkdir "$targetdir/mnt/hostrun";
+       syscmd("mount --bind /run $targetdir/mnt/hostrun") == 0 ||
+           die "unable to bindmount run on $targetdir/mnt/hostrun\n";
+
        update_progress(1, 0.05, $maxper, "extracting base system");
 
        my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat ($basefile);
@@ -1315,6 +1441,12 @@ sub extract_data {
            die "unable to mount proc on $targetdir/proc\n";
        syscmd("mount -n -t sysfs sysfs $targetdir/sys") == 0 ||
            die "unable to mount sysfs on $targetdir/sys\n";
+       if ($boot_type eq 'efi') {
+           syscmd("mount -n -t efivarfs efivarfs $targetdir/sys/firmware/efi/efivars") == 0 ||
+               die "unable to mount efivarfs on $targetdir/sys/firmware/efi/efivars: $!\n";
+       }
+       syscmd("chroot $targetdir mount --bind /mnt/hostrun /run") == 0 ||
+           die "unable to re-bindmount hostrun on /run in chroot\n";
 
        update_progress(1, $maxper, 0.5, "configuring base system");
 
@@ -1407,9 +1539,10 @@ sub extract_data {
        # Note: this is required by current grub, but really dangerous, because
        # vfat does not have journaling, so it triggers manual fsck after each crash
        # so we only mount /boot/efi if really required (efi systems).
-       if ($grub_plattform =~ m/^efi-/) {
+       if ($boot_type eq 'efi' && !$use_zfs) {
            if (scalar(@$bootdevinfo)) {
                my $di = @$bootdevinfo[0]; # simply use first disk
+
                if ($di->{esp}) {
                    my $efi_boot_uuid = $di->{esp};
                    if (my $uuid = find_dev_by_uuid ($di->{esp})) {
@@ -1440,6 +1573,13 @@ sub extract_data {
        diversion_add($targetdir, "/usr/sbin/update-grub", "/bin/true");
        diversion_add($targetdir, "/usr/sbin/update-initramfs", "/bin/true");
 
+       my $machine_id = run_command("systemd-id128 new");
+       die "unable to create a new machine-id\n" if ! $machine_id;
+       write_config($machine_id, "$targetdir/etc/machine-id");
+
+       syscmd("cp /etc/hostid $targetdir/etc/") == 0 ||
+           die "unable to copy hostid\n";
+
        syscmd("touch  $targetdir/proxmox_install_mode");
 
        my $grub_install_devices_txt = '';
@@ -1473,10 +1613,6 @@ _EOD
            chomp;
            my $path = $_;
            my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
-#          if ($deb =~ m/^grub-efi-/ && $deb !~ m/^grub-${grub_plattform}/) {
-#              $count++;
-#              next;
-#          }
            update_progress($count/$pkg_count, 0.5, 0.75, "extracting $deb");
            print "extracting: $deb\n";
            syscmd("cp $path $targetdir/tmp/$deb") == 0 ||
@@ -1554,6 +1690,10 @@ _EOD
            syscmd("sed -i -e 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"root=ZFS=$zfspoolname\\/ROOT\\/$zfsrootvolname boot=zfs\"/' $targetdir/etc/default/grub") == 0 ||
                die "unable to update /etc/default/grub\n";
 
+           if ($boot_type eq 'efi') {
+               write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs", "$targetdir/etc/kernel/cmdline");
+           }
+
        }
 
        diversion_remove($targetdir, "/usr/sbin/update-grub");
@@ -1574,46 +1714,47 @@ _EOD
            symlink ("/proc/mounts", "$targetdir/etc/mtab");
            syscmd("mount -n --bind /dev $targetdir/dev");
 
-           syscmd("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
-               die "unable to install initramfs\n";
-
-           foreach my $di (@$bootdevinfo) {
-               my $dev = $di->{devname};
-               syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
-                       die "unable to install the i386-pc boot loader on '$dev'\n";
-
-               if ($di->{esp}) {
-                   syscmd("mount -n $di->{esp} -t vfat $targetdir/boot/efi") == 0 ||
-                       die "unable to mount $di->{esp}\n";
-                   my $rc = syscmd("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev");
-                   if ($rc != 0) {
-                       if (-d '/sys/firmware/efi') {
-                           die "unable to install the EFI boot loader on '$dev'\n";
-                       } else {
-                           warn "unable to install the EFI boot loader on '$dev', ignoring (not booted using UEFI)\n";
+           my $bootloader_err_list = [];
+           eval {
+               syscmd("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
+                   die "unable to install initramfs\n";
+
+               foreach my $di (@$bootdevinfo) {
+                   my $dev = $di->{devname};
+                   eval {
+                       syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
+                               die "unable to install the i386-pc boot loader on '$dev'\n";
+                   };
+                   push @$bootloader_err_list, $@ if $@;
+
+                   eval {
+                       if (my $esp = $di->{esp}) {
+                           if ($use_zfs) {
+                               prepare_systemd_boot_esp($esp, $targetdir);
+                           } else {
+                               prepare_grub_efi_boot_esp($dev, $esp, $targetdir);
+                           }
                        }
-                   }
-                   # also install fallback boot file (OVMF does not boot without)
-                   mkdir("$targetdir/boot/efi/EFI/BOOT");
-                   syscmd("cp $targetdir/boot/efi/EFI/proxmox/grubx64.efi $targetdir/boot/efi/EFI/BOOT/BOOTx64.EFI") == 0 ||
-                       die "unable to copy efi boot loader\n";
-
-                   syscmd("umount $targetdir/boot/efi") == 0 ||
-                       die "unable to umount $targetdir/boot/efi\n";
+                   };
+                   push @$bootloader_err_list, $@ if $@;
                }
-           }
 
-           syscmd("chroot $targetdir /usr/sbin/update-grub") == 0 ||
-               die "unable to update boot loader config\n";
+               syscmd("chroot $targetdir /usr/sbin/update-grub") == 0 ||
+                   die "unable to update boot loader config\n";
+           };
+           push @$bootloader_err_list, $@ if $@;
+
+           if (scalar(@$bootloader_err_list) > 0) {
+               $bootloader_err = "bootloader setup errors:\n";
+               map { $bootloader_err .= "- $_" } @$bootloader_err_list;
+               warn $bootloader_err;
+           }
 
            syscmd("umount $targetdir/dev");
        }
 
        # cleanup
 
-       # hack: remove dead.letter from sshd installation
-       syscmd("rm -rf $targetdir/dead.letter");
-
        unlink "$targetdir/usr/sbin/policy-rc.d";
 
        diversion_remove($targetdir, "/sbin/start-stop-daemon");
@@ -1675,8 +1816,11 @@ _EOD
        syscmd("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> final.pkglist");
     }
 
+    syscmd("umount $targetdir/run");
+    syscmd("umount $targetdir/mnt/hostrun");
     syscmd("umount $targetdir/tmp");
     syscmd("umount $targetdir/proc");
+    syscmd("umount $targetdir/sys/firmware/efi/efivars");
     syscmd("umount $targetdir/sys");
 
     if ($use_zfs) {
@@ -1698,6 +1842,10 @@ _EOD
        syscmd("zpool export $zfspoolname");
     }
 
+    if ($bootloader_err) {
+       $err = $err ? "$err\n$bootloader_err" : $bootloader_err;
+    }
+
     die $err if $err;
 }
 
@@ -1739,13 +1887,20 @@ sub display_html {
     my $data = file_get_contents($path);
 
     if ($filename eq 'license.htm') {
-       my $license = decode('utf8', file_get_contents("${proxmox_cddir}/EULA"));
+       my $license = eval { decode('utf8', file_get_contents("${proxmox_cddir}/EULA")) };
+       if (my $err = $@) {
+           die $err if !$opt_testmode;
+           $license = "TESTMODE: Ignore non existent EULA...\n";
+       }
        my $title = "END USER LICENSE AGREEMENT (EULA)";
        $data =~ s/__LICENSE__/$license/;
        $data =~ s/__LICENSE_TITLE__/$title/;
+    } elsif ($filename eq 'success.htm') {
+       my $addr = $ipversion == 6 ? "[${ipaddress}]" : "$ipaddress";
+       $data =~ s/\@IPADDR\@/$addr/;
     }
 
-    $htmlview->load_html_string($data, $url);
+    $htmlview->load_html($data, $url);
 
     $last_display_change = time();
 }
@@ -1819,7 +1974,7 @@ sub create_main_window {
     my $vbox2 = Gtk3::VBox->new(0, 0);
     $hbox->add($vbox2);
 
-    $htmlview = Gtk3::WebKit::WebView->new();
+    $htmlview = Gtk3::WebKit2::WebView->new();
     my $scrolls = Gtk3::ScrolledWindow->new();
     $scrolls->add($htmlview);
 
@@ -2023,7 +2178,11 @@ sub create_ipconf_view {
 
     my $device_change_handler = sub {
        my $current = shift;
-       $ipconf->{selected} = $device_active_map->{$current->get_active()};
+
+       my $new = $device_active_map->{$current->get_active()};
+       return if defined($ipconf->{selected}) && $new eq $ipconf->{selected};
+
+       $ipconf->{selected} = $new;
        my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
        $config->{mngmt_nic} = $iface->{name};
        $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
@@ -2196,9 +2355,9 @@ sub create_ack_view {
     my $html_data = file_get_contents($ack_template);
 
     my %config_values = (
-       __target_hd__ => $target_hd,
+       __target_hd__ => join(' | ', @{$config_options->{target_hds}}),
        __target_fs__ => $config_options->{filesys},
-       __country__ => $country,
+       __country__ => $cmap->{country}->{$country}->{name},
        __timezone__ => $timezone,
        __keymap__ => $keymap,
        __mailto__ => $mailto,
@@ -2555,7 +2714,7 @@ sub create_country_view {
 my $target_hd_combo;
 my $target_hd_label;
 
-my $hdopion_first_setup = 1;
+my $hdoption_first_setup = 1;
 
 my $create_basic_grid = sub {
     my $grid =  Gtk3::Grid->new();
@@ -2611,7 +2770,7 @@ my $create_raid_disk_grid = sub {
            });
        }
 
-       if ($hdopion_first_setup) {
+       if ($hdoption_first_setup) {
            $disk_selector->set_active ($i+1) if $hds->[$i];
        } else {
            my $hdind = 0;
@@ -2776,6 +2935,12 @@ sub create_hdoption_view {
     $grid->attach($sep, 0, $row, 2, 1);
     $row++;
 
+    my $hw_raid_note = Gtk3::Label->new("Note: ZFS is not compatible with disks backed by a hardware RAID controller. For details see the reference documentation.");
+    $hw_raid_note->set_line_wrap(1);
+    $hw_raid_note->set_max_width_chars(30);
+    $hw_raid_note->set_visible(0);
+    $grid->attach($hw_raid_note, 0, $row++, 2, 1);
+
     my $hdsize_labeled_widgets = [];
 
     # size compute
@@ -2834,7 +2999,7 @@ sub create_hdoption_view {
     $grid->attach($options_stack, 0, $row, 2, 1);
     $row++;
 
-    $hdopion_first_setup = 0;
+    $hdoption_first_setup = 0;
 
     my $switch_view = sub {
        my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
@@ -2843,6 +3008,7 @@ sub create_hdoption_view {
        $target_hd_combo->set_visible(!$raid);
        $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
        $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
+       $hw_raid_note->set_visible($raid);
        $options_stack_switcher->set_visible($enable_zfs_opts);
        $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
        if ($raid) {
@@ -2864,6 +3030,10 @@ sub create_hdoption_view {
        &$switch_view();
     });
 
+    my $sep2 = Gtk3::HSeparator->new();
+    $sep2->set_visible(1);
+    $contarea->pack_end($sep2, 1, 1, 10);
+
     $dialog->show();
 
     $dialog->run();
@@ -3031,6 +3201,7 @@ sub get_btrfs_raid_setup {
     return ($devlist, $mode);
 }
 
+my $last_hd_selected = 0;
 sub create_hdsel_view {
 
     $prev_btn->set_sensitive(1); # enable previous button at this point
@@ -3043,7 +3214,7 @@ sub create_hdsel_view {
     $vbox->pack_start($hbox, 0, 0, 10);
 
     my ($disk, $devname, $size, $model) = @{@$hds[0]};
-    $target_hd = $devname;
+    $target_hd = $devname if !defined($target_hd);
 
     $target_hd_label = Gtk3::Label->new("Target Harddisk: ");
     $hbox->pack_start($target_hd_label, 0, 0, 0);
@@ -3055,10 +3226,17 @@ sub create_hdsel_view {
        $target_hd_combo->append_text (get_device_desc($devname, $size, $model));
     }
 
-    $target_hd_combo->set_active(0);
+    my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
+    if ($raid) {
+       $target_hd_label->set_text("Target: $config_options->{filesys} ");
+       $target_hd_combo->set_visible(0);
+       $target_hd_combo->set_no_show_all(1);
+    }
+    $target_hd_combo->set_active($last_hd_selected);
     $target_hd_combo->signal_connect(changed => sub {
        $a = shift->get_active;
        my ($disk, $devname) = @{@$hds[$a]};
+       $last_hd_selected = $a;
        $target_hd = $devname;
     });
 
@@ -3076,27 +3254,25 @@ sub create_hdsel_view {
     set_next(undef, sub {
 
        if ($config_options->{filesys} =~ m/zfs/) {
-           eval { get_zfs_raid_setup(); };
+           my ($devlist) = eval { get_zfs_raid_setup() };
            if (my $err = $@) {
-               display_message("Warning: $err\n" .
-                               "Please fix ZFS setup first.");
-           } else {
-               $step_number++;
-               create_country_view();
+               display_message("Warning: $err\nPlease fix ZFS setup first.");
+               return;
            }
+           $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
        } elsif ($config_options->{filesys} =~ m/btrfs/) {
-           eval { get_btrfs_raid_setup(); };
+           my ($devlist) = eval { get_btrfs_raid_setup() };
            if (my $err = $@) {
-               display_message("Warning: $err\n" .
-                               "Please fix BTRFS setup first.");
-           } else {
-               $step_number++;
-               create_country_view();
+               display_message("Warning: $err\nPlease fix BTRFS setup first.");
+               return;
            }
+           $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
        } else {
-           $step_number++;
-           create_country_view();
+           $config_options->{target_hds} = [ $target_hd ];
        }
+
+       $step_number++;
+       create_country_view();
     });
 }
 
@@ -3155,11 +3331,18 @@ sub create_intro_view {
 
     cleanup_view();
 
+    if (int($total_memory) < 1024) {
+       my $fullname = $product_fullname->{$setup->{product}};
+
+       display_error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
+           "See 'System Requirements' in the $fullname documentation.");
+    }
+
     if ($setup->{product} eq 'pve') {
        eval {
            my $cpuinfo = file_get_contents('/proc/cpuinfo');
            if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
-               display_error("No support for KVM virtualisation detected.\n\n" .
+               display_error("No support for KVM virtualization detected.\n\n" .
                              "Check BIOS settings for Intel VT / AMD-V / SVM.")
            }
        };