]> git.proxmox.com Git - pve-installer.git/blobdiff - proxinstall
bump version to 8.1.0
[pve-installer.git] / proxinstall
index 41926bb7ba3da23e9b487a27b7b7535536ae85e1..857281d11d437caba6e943ad8bd45a6f49db5e51 100755 (executable)
@@ -3,6 +3,7 @@
 use strict;
 use warnings;
 
+use Encode;
 use Getopt::Long;
 use IO::File;
 use Glib;
@@ -23,13 +24,11 @@ Proxmox::Log::init("/tmp/install.log");
     Proxmox::Install::ISOEnv::set_test_image($test_image) if $test_image;
 }
 
-
 use Proxmox::Install::ISOEnv;
 use Proxmox::Install::RunEnv;
 
 # init singletons TODO: avoid all global initialization, use single "main" method
 my $iso_env = Proxmox::Install::ISOEnv::get();
-my $run_env = Proxmox::Install::RunEnv::get();
 
 use Proxmox::Install;
 use Proxmox::Install::Config;
@@ -181,7 +180,19 @@ sub create_main_window {
     my $abort = Gtk3::Button->new('_Abort');
     $abort->set_can_focus(0);
     $cmdbox->pack_start($abort, 0, 0, 10);
-    $abort->signal_connect(clicked => sub { app_quit(-1); });
+    $abort->signal_connect(clicked => sub {
+       my $msg = 'Abort Installation';
+       my $secondary_text = 'Are you sure you want to abort the installation?';
+       my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'yes-no', $msg);
+       $dialog->format_secondary_text($secondary_text);
+       $dialog->signal_connect(response => sub {
+           my ($dialog, $response) = @_;
+
+           $dialog->close();
+           app_quit(-1) if $response eq 'yes';
+       });
+       $dialog->present();
+    });
 
     my $vbox2 = Gtk3::Box->new('vertical', 0);
     $hbox->add($vbox2);
@@ -214,6 +225,8 @@ sub create_main_window {
     $gtk_state->{next_btn} = $next_btn;
     $gtk_state->{progress_bar} = Gtk3::ProgressBar->new();
     $gtk_state->{progress_status} = Gtk3::Label->new('');
+    $gtk_state->{abort_btn} = $abort;
+    $gtk_state->{disk_selection} = {};
 
     Proxmox::UI::init_gtk($gtk_state, $iso_env);
 
@@ -328,15 +341,26 @@ sub create_ipconf_view {
 
     my ($cidr_box, $ipconf_entry_addr, $ipconf_entry_mask) = create_cidr_inputs($cidr);
 
-    my $device_cb = Gtk3::ComboBoxText->new();
+    my $device_model = Gtk3::ListStore->new('Glib::String', 'Glib::String');
+    my $device_cb = Gtk3::ComboBox->new_with_model($device_model);
     $device_cb->set_active(0);
     $device_cb->set_visible(1);
 
+    my $icon_cell = Gtk3::CellRendererText->new();
+    $device_cb->pack_start($icon_cell, 0);
+    $device_cb->add_attribute($icon_cell, 'text', 0);
+    $icon_cell->set_property('foreground', 'green');
+
+    my $cell = Gtk3::CellRendererText->new();
+    $device_cb->pack_start($cell, 0);
+    $device_cb->add_attribute($cell, 'text', 1);
+
     my $get_device_desc = sub {
        my $iface = shift;
        return "$iface->{name} - $iface->{mac} ($iface->{driver})";
     };
 
+    my $run_env = Proxmox::Install::RunEnv::get();
     my $ipconf = $run_env->{ipconf};
 
     my ($device_active_map, $device_active_reverse_map) = ({}, {});
@@ -360,7 +384,12 @@ sub create_ipconf_view {
     my $i = 0;
     for my $index (sort keys $ipconf->{ifaces}->%*) {
        my $iface = $ipconf->{ifaces}->{$index};
-       $device_cb->append_text($get_device_desc->($iface));
+       my $iter = $device_model->append();
+       my $symbol = "$iface->{state}" eq "UP" ? "\x{25CF}" : ' ';
+       $device_model->set($iter,
+          0 => $symbol,
+          1 => $get_device_desc->($iface),
+       );
        $device_active_map->{$i} = $index;
        $device_active_reverse_map->{$iface->{name}} = $i;
        if ($ipconf_first_view && $index == $ipconf->{default}) {
@@ -388,9 +417,11 @@ sub create_ipconf_view {
     $vbox->pack_start($devicebox, 0, 0, 2);
 
     my $fqdn = Proxmox::Install::Config::get_fqdn();
-    my $hn = $fqdn // "$iso_env->{product}." . ($ipconf->{domain} // "example.invalid");
+    my $hostname = $run_env->{network}->{hostname} || $iso_env->{product};
+    my $domain = $ipconf->{domain} || "example.invalid";
+    $fqdn //= "$hostname.$domain";
 
-    my ($hostbox, $hostentry) = create_text_input($hn, 'Hostname (FQDN):');
+    my ($hostbox, $hostentry) = create_text_input($fqdn, 'Hostname (FQDN):');
     $vbox->pack_start($hostbox, 0, 0, 2);
 
     $vbox->pack_start($cidr_box, 0, 0, 2);
@@ -415,8 +446,6 @@ sub create_ipconf_view {
        $text =~ s/^\s+//;
        $text =~ s/\s+$//;
 
-       my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
-
        # Debian does not support purely numeric hostnames
        if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
            Proxmox::UI::message("Purely numeric hostnames are not allowed.");
@@ -424,8 +453,11 @@ sub create_ipconf_view {
            return;
        }
 
-       if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
-           $text =~ m/^([^\.]+)\.(\S+)$/) {
+       if ($text
+           && $text =~ m/^${Proxmox::Sys::Net::FQDN_RE}$/
+           && $text !~ m/.example.invalid$/
+           && $text =~ m/^([^\.]+)\.(\S+)$/
+       ) {
            Proxmox::Install::Config::set_hostname($1);
            Proxmox::Install::Config::set_domain($2);
        } else {
@@ -467,8 +499,8 @@ sub create_ipconf_view {
        $text = $ipconf_entry_dns->get_text();
        my ($dns_ip, $dns_ip_version) = parse_ip_address($text);
        if (!defined($dns_ip) || $dns_ip_version != $ipversion) {
-           my $msg = defined($gateway_ip)
-               ? "DNS and host IP version must not differ (IPv$gateway_ip_version != IPv$ipversion)."
+           my $msg = defined($dns_ip)
+               ? "DNS and host IP version must not differ (IPv$dns_ip_version != IPv$ipversion)."
                : "DNS IP is not valid.";
            Proxmox::UI::message($msg);
            $ipconf_entry_dns->grab_focus();
@@ -953,8 +985,7 @@ my $get_selected_hdsize = sub {
     my $cached_disks = get_cached_disks();
     my $disk_count = scalar(@$cached_disks);
     for (my $i = 0; $i < $disk_count; $i++) {
-       next if !Proxmox::Install::Config::get_disk_selection($i);
-       my $cur_hd = $cached_disks->[$i];
+       my $cur_hd = $gtk_state->{disk_selection}->{$i} // next;
        my $disksize = int(@$cur_hd[2] / (2 * 1024 * 1024.0)); # size in GB
        $hdsize //= $disksize;
        $hdsize = $disksize if $disksize < $hdsize;
@@ -1019,7 +1050,7 @@ my $create_raid_disk_grid = sub {
            my $w = shift;
            my $diskid = $w->{pve_disk_id};
            my $a = $w->get_active - 1;
-           Proxmox::Install::Config::set_disk_selection($diskid, $a >= 0);
+           $gtk_state->{disk_selection}->{$diskid} = ($a >= 0) ? $cached_disks->[$a] : undef;
            for my $btn (@$hdsize_buttons) {
                update_hdsize_adjustment($btn->get_adjustment());
            }
@@ -1029,8 +1060,7 @@ my $create_raid_disk_grid = sub {
            $disk_selector->set_active ($i+1) if $cached_disks->[$i];
        } else {
            my $hdind = 0;
-           if (Proxmox::Install::Config::get_disk_selection($i)) {
-               my $cur_hd = $cached_disks->[$i];
+           if (my $cur_hd = $gtk_state->{disk_selection}->{$i}) {
                foreach my $hd (@$cached_disks) {
                    if (@$hd[1] eq @$cur_hd[1]) {
                        $disk_selector->set_active($hdind+1);
@@ -1132,6 +1162,21 @@ my $create_raid_advanced_grid = sub {
     $spinbutton_copies->set_value($copies);
     push @$labeled_widgets, "copies", $spinbutton_copies;
 
+    if ($iso_env->{product} eq 'pve') {
+       my $total_memory = Proxmox::Install::RunEnv::get('total_memory');
+
+       my $spinbutton_arc_max = Gtk3::SpinButton->new_with_range(
+           $Proxmox::Install::RunEnv::ZFS_ARC_MIN_SIZE_MIB, $total_memory, 1);
+       $spinbutton_arc_max->set_tooltip_text('Maximum ARC size in megabytes');
+       $spinbutton_arc_max->signal_connect('value-changed' => sub {
+           my $w = shift;
+           Proxmox::Install::Config::set_zfs_opt('arc_max', $w->get_value_as_int());
+       });
+       my $arc_max = Proxmox::Install::Config::get_zfs_opt('arc_max');
+       $spinbutton_arc_max->set_value($arc_max);
+       push @$labeled_widgets, "ARC max size", $spinbutton_arc_max;
+    }
+
     push @$labeled_widgets, "hdsize", $hdsize_btn;
     return $create_label_widget_grid->($labeled_widgets);;
 };
@@ -1383,121 +1428,12 @@ sub create_hdoption_view {
     $dialog->destroy();
 }
 
-my $get_raid_devlist = sub {
-
-    my $dev_name_hash = {};
-
-    my $cached_disks = get_cached_disks();
-    my $devlist = [];
-    for (my $i = 0; $i < @$cached_disks; $i++) {
-       next if !Proxmox::Install::Config::get_disk_selection($i);
-
-       my $hd = $cached_disks->[$i];
-       my ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
-       die "device '$devname' is used more than once\n" if $dev_name_hash->{$devname};
-       $dev_name_hash->{$devname} = $hd;
-       push @$devlist, $hd;
-    }
-
-    return $devlist;
-};
-
-sub zfs_mirror_size_check {
-    my ($expected, $actual) = @_;
-
-    die "mirrored disks must have same size\n"
-       if abs($expected - $actual) > $expected / 10;
-}
-
-sub legacy_bios_4k_check {
-    my ($lbs) = @_;
-    die "Booting from 4kn drive in legacy BIOS mode is not supported.\n"
-       if $run_env->{boot_type} ne 'efi' && $lbs == 4096;
-}
-
-sub get_zfs_raid_setup {
-    my $filesys = Proxmox::Install::Config::get_filesys();
-
-    my $devlist = &$get_raid_devlist();
-
-    my $diskcount = scalar(@$devlist);
-    die "$filesys needs at least one device\n" if $diskcount < 1;
-
-    my $cmd= '';
-    if ($filesys eq 'zfs (RAID0)') {
-       foreach my $hd (@$devlist) {
-           legacy_bios_4k_check(@$hd[4]);
-           $cmd .= " @$hd[1]";
-       }
-    } elsif ($filesys eq 'zfs (RAID1)') {
-       die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
-       $cmd .= ' mirror ';
-       my $hd = @$devlist[0];
-       my $expected_size = @$hd[2]; # all disks need approximately same size
-       foreach my $hd (@$devlist) {
-           zfs_mirror_size_check($expected_size, @$hd[2]);
-           legacy_bios_4k_check(@$hd[4]);
-           $cmd .= " @$hd[1]";
-       }
-    } elsif ($filesys eq 'zfs (RAID10)') {
-       die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
-       die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
-
-       for (my $i = 0; $i < $diskcount; $i+=2) {
-           my $hd1 = @$devlist[$i];
-           my $hd2 = @$devlist[$i+1];
-           zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
-           legacy_bios_4k_check(@$hd1[4]);
-           legacy_bios_4k_check(@$hd2[4]);
-           $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
-       }
-
-    } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
-       my $level = $1;
-       my $mindisks = 2 + $level;
-       die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
-       my $hd = @$devlist[0];
-       my $expected_size = @$hd[2]; # all disks need approximately same size
-       $cmd .= " raidz$level";
-       foreach my $hd (@$devlist) {
-           zfs_mirror_size_check($expected_size, @$hd[2]);
-           legacy_bios_4k_check(@$hd[4]);
-           $cmd .= " @$hd[1]";
-       }
-    } else {
-       die "unknown zfs mode '$filesys'\n";
-    }
-
-    return ($devlist, $cmd);
-}
-
-sub get_btrfs_raid_setup {
-    my $filesys = Proxmox::Install::Config::get_filesys();
-
-    my $devlist = &$get_raid_devlist();
-
-    my $diskcount = scalar(@$devlist);
-    die "$filesys needs at least one device\n" if $diskcount < 1;
-
-    my $mode;
-
-    if ($diskcount == 1) {
-       $mode = 'single';
-    } else {
-       if ($filesys eq 'btrfs (RAID0)') {
-           $mode = 'raid0';
-       } elsif ($filesys eq 'btrfs (RAID1)') {
-           die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
-           $mode = 'raid1';
-       } elsif ($filesys eq 'btrfs (RAID10)') {
-           die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
-           $mode = 'raid10';
-       } else {
-           die "unknown btrfs mode '$filesys'\n";
-       }
+sub apply_raid_disk_selection {
+    Proxmox::Install::Config::set_key('disk_selection', {}); # reset
+    for my $order (sort keys $gtk_state->{disk_selection}->%*) {
+       my $disk = $gtk_state->{disk_selection}->{$order} // next;
+       Proxmox::Install::Config::set_disk_selection($order, $disk->[0]);
     }
-
-    return ($devlist, $mode);
 }
 
 my $last_hd_selected = 0;
@@ -1557,14 +1493,16 @@ sub create_hdsel_view {
     set_next(undef, sub {
        my $filesys = Proxmox::Install::Config::get_filesys();
        if ($filesys =~ m/zfs/) {
-           my ($devlist) = eval { get_zfs_raid_setup() };
+           apply_raid_disk_selection();
+           my ($devlist) = eval { Proxmox::Install::get_zfs_raid_setup() };
            if (my $err = $@) {
                Proxmox::UI::message("Warning: $err\nPlease fix ZFS setup first.");
                return;
            }
            $target_hds = [ map { $_->[1] } @$devlist ];
        } elsif ($filesys =~ m/btrfs/) {
-           my ($devlist) = eval { get_btrfs_raid_setup() };
+           apply_raid_disk_selection();
+           my ($devlist) = eval { Proxmox::Install::get_btrfs_raid_setup() };
            if (my $err = $@) {
                Proxmox::UI::message("Warning: $err\nPlease fix BTRFS setup first.");
                return;
@@ -1574,7 +1512,7 @@ sub create_hdsel_view {
            my $target_hd = Proxmox::Install::Config::get_target_hd();
            eval {
                my $target_block_size = Proxmox::Sys::Block::logical_blocksize($target_hd);
-               legacy_bios_4k_check($target_block_size);
+               Proxmox::Install::legacy_bios_4k_check($target_block_size);
            };
            if (my $err = $@) {
                Proxmox::UI::message("Warning: $err\n");
@@ -1639,6 +1577,10 @@ sub create_extract_view {
        return $raw_html;
     };
 
+    # It does not make sense to Abort the install at this point, whether it
+    # succeded or failed makes no difference.
+    $gtk_state->{abort_btn}->set_sensitive(0);
+
     if ($err) {
        Proxmox::UI::display_html("fail.htm");
        # suppress "empty" error as we got some case where the user choose to abort on a prompt,
@@ -1653,6 +1595,7 @@ sub create_extract_view {
                if ($autoreboot_seconds > 0) {
                    $autoreboot_seconds--;
                    Proxmox::UI::display_html("success.htm", $success_transform);
+                   return 1; # re-schedule, undef isn't enough anymore since Bookworm
                } else {
                    app_quit(0);
                }
@@ -1667,19 +1610,17 @@ sub create_intro_view {
 
     cleanup_view();
 
+    my $run_env = Proxmox::Install::RunEnv::get();
     if (int($run_env->{total_memory}) < 1024) {
        Proxmox::UI::error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
            "See 'System Requirements' in the $iso_env->{cfg}->{fullname} documentation.");
     }
 
-    if ($iso_env->{product} eq 'pve') {
-       my $cpuinfo = eval { file_read_all('/proc/cpuinfo') };
-       if (!$cpuinfo || $cpuinfo !~ /^flags\s*:.*(vmx|svm)/m) {
-           Proxmox::UI::error(
-               "No support for hardware-accelerated KVM virtualization detected.\n\n"
-               ."Check BIOS settings for Intel VT / AMD-V / SVM."
-           );
-       }
+    if ($iso_env->{product} eq 'pve' && !$run_env->{hvm_supported}) {
+       Proxmox::UI::error(
+           "No support for hardware-accelerated KVM virtualization detected.\n\n"
+           ."Check BIOS settings for Intel VT / AMD-V / SVM."
+       );
     }
 
     Proxmox::UI::display_html('license.htm', sub {
@@ -1702,9 +1643,10 @@ sub create_intro_view {
     set_next("I a_gree", \&create_hdsel_view);
 }
 
+log_info("initializing GTK and creating main window...");
 Gtk3::init();
 
-create_main_window ();
+create_main_window();
 
 my $initial_error = 0;
 
@@ -1724,6 +1666,7 @@ my $initial_error = 0;
     }
 }
 
+my $run_env = Proxmox::Install::RunEnv::get();
 if (!$initial_error && (scalar keys $run_env->{ipconf}->{ifaces}->%* == 0)) {
     print STDERR "no network interfaces found\n";
     $initial_error = 1;