X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=proxinstall;h=3e247293bd7a5298abb44339747527c08e1a56ca;hb=refs%2Fheads%2Fmaster;hp=590118f7cec9fcc95db483536f745844f7567ab1;hpb=a677c773e9856c7c8cbd20916870cea3d86c48b2;p=pve-installer.git diff --git a/proxinstall b/proxinstall index 590118f..12f3eaa 100755 --- a/proxinstall +++ b/proxinstall @@ -3,17 +3,12 @@ use strict; use warnings; +use Encode; use Getopt::Long; -use IPC::Open2; use IO::File; -use Cwd 'abs_path'; use Glib; use Gtk3; use Gtk3::WebKit2; -use Encode; -use File::Basename; -use File::Path; -use Time::HiRes; use POSIX ":sys_wait_h"; use JSON; @@ -29,21 +24,18 @@ 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 +# 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; -use Proxmox::Install::StorageConfig; -use Proxmox::Sys::Block qw(get_cached_disks wipe_disk partition_bootable_disk); -use Proxmox::Sys::Command qw(run_command syscmd); -use Proxmox::Sys::File qw(file_read_firstline file_read_all file_write_all); +use Proxmox::Sys::Block qw(get_cached_disks); +use Proxmox::Sys::Command qw(syscmd); +use Proxmox::Sys::File qw(file_read_all file_write_all); use Proxmox::Sys::Net qw(parse_ip_address parse_ip_mask); use Proxmox::UI; @@ -97,7 +89,6 @@ my @steps = ( my $gtk_state = {}; my $target_hds; # only for the summary view -my $autoreboot_seconds = 5; sub app_quit { my ($exit_code) = @_; @@ -189,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); @@ -222,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); @@ -276,18 +281,14 @@ sub check_number { sub create_text_input { my ($default, $text) = @_; - my $hbox = Gtk3::Box->new('horizontal', 0); - my $label = Gtk3::Label->new($text); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox->pack_start($label, 0, 0, 10); my $e1 = Gtk3::Entry->new(); $e1->set_width_chars(35); - $hbox->pack_start($e1, 0, 0, 0); $e1->set_text($default); - return ($hbox, $e1); + return ($label, $e1); } sub create_cidr_inputs { my ($cidr) = @_; @@ -299,52 +300,76 @@ sub create_cidr_inputs { my $label = Gtk3::Label->new('IP Address (CIDR)'); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox->pack_start($label, 0, 0, 10); my $ip_el = Gtk3::Entry->new(); $ip_el->set_width_chars(28); - $hbox->pack_start($ip_el, 0, 0, 0); + $hbox->pack_start($ip_el, 1, 1, 0); $ip_el->set_text($default_ip); - $label = Gtk3::Label->new('/'); - $label->set_size_request(10, -1); - $hbox->pack_start($label, 0, 0, 2); + my $dash_label = Gtk3::Label->new('/'); + $dash_label->set_size_request(10, -1); + $hbox->pack_start($dash_label, 0, 0, 2); my $cidr_el = Gtk3::Entry->new(); $cidr_el->set_width_chars(3); $hbox->pack_start($cidr_el, 0, 0, 0); $cidr_el->set_text($default_mask); - return ($hbox, $ip_el, $cidr_el); + return ($label, $hbox, $ip_el, $cidr_el); } my $ipconf_first_view = 1; +my $create_basic_grid = sub { + my $grid = Gtk3::Grid->new(); + $grid->set_visible(1); + $grid->set_column_spacing(10); + $grid->set_row_spacing(10); + $grid->set_hexpand(1); + + $grid->set_margin_start(20); + $grid->set_margin_end(20); + $grid->set_margin_top(10); + $grid->set_margin_bottom(10); + + return $grid; +}; + sub create_ipconf_view { cleanup_view(); Proxmox::UI::display_html('ipconf.htm'); - my $vcontainer = Gtk3::Box->new('vertical', 0); - $gtk_state->{inbox}->pack_start($vcontainer, 1, 0, 0); - my $hcontainer = Gtk3::Box->new('horizontal', 0); - $vcontainer->pack_start($hcontainer, 0, 0, 10); - my $vbox = Gtk3::Box->new('vertical', 0); - $hcontainer->add($vbox); + my $grid = &$create_basic_grid(); + $grid->set_row_spacing(10); + $grid->set_column_spacing(10); + + $gtk_state->{inbox}->pack_start($grid, 0, 0, 0); my $cidr = Proxmox::Install::Config::get_cidr() // '192.168.100.2/24'; - my ($cidr_box, $ipconf_entry_addr, $ipconf_entry_mask) = create_cidr_inputs($cidr); + my ($cidr_label, $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) = ({}, {}); @@ -368,7 +393,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}) { @@ -386,35 +416,39 @@ sub create_ipconf_view { $device_cb->set_active(0); } - my $devicebox = Gtk3::Box->new('horizontal', 0); - my $label = Gtk3::Label->new("Management Interface:"); + my $label = Gtk3::Label->new("Management Interface"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $devicebox->pack_start($label, 0, 0, 10); - $devicebox->pack_start($device_cb, 0, 0, 0); - $vbox->pack_start($devicebox, 0, 0, 2); + $grid->attach($label, 0, 0, 1, 1); + $grid->attach($device_cb, 1, 0, 1, 1); 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):'); - $vbox->pack_start($hostbox, 0, 0, 2); + my ($host_label, $hostentry) = create_text_input($fqdn, 'Hostname (FQDN)'); + $grid->attach($host_label, 0, 1, 1, 1); + $grid->attach($hostentry, 1, 1, 1, 1); - $vbox->pack_start($cidr_box, 0, 0, 2); + $grid->attach($cidr_label, 0, 2, 1, 1); + $grid->attach($cidr_box, 1, 2, 1, 1); my $cfg_gateway = Proxmox::Install::Config::get_gateway(); my $gateway = $cfg_gateway // $ipconf->{gateway} || '192.168.100.1'; - my ($gwbox, $ipconf_entry_gw) = create_text_input($gateway, 'Gateway:'); - $vbox->pack_start($gwbox, 0, 0, 2); + my ($gw_label, $ipconf_entry_gw) = create_text_input($gateway, 'Gateway'); + $grid->attach($gw_label, 0, 3, 1, 1); + $grid->attach($ipconf_entry_gw, 1, 3, 1, 1); my $cfg_dns = Proxmox::Install::Config::get_dns(); my $dnsserver = $cfg_dns // $ipconf->{dnsserver} || $gateway; - my ($dnsbox, $ipconf_entry_dns) = create_text_input($dnsserver, 'DNS Server:'); + my ($dns_label, $ipconf_entry_dns) = create_text_input($dnsserver, 'DNS Server'); - $vbox->pack_start($dnsbox, 0, 0, 0); + $grid->attach($dns_label, 0, 4, 1, 1); + $grid->attach($ipconf_entry_dns, 1, 4, 1, 1); $gtk_state->{inbox}->show_all; set_next(undef, sub { @@ -423,23 +457,16 @@ sub create_ipconf_view { $text =~ s/^\s+//; $text =~ s/\s+$//; - my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)"; + my ($hostname, $domainname) = eval { Proxmox::Sys::Net::parse_fqdn($text) }; + my $err = $@; - # Debian does not support purely numeric hostnames - if ($text && $text =~ /^[0-9]+(?:\.|$)/) { - Proxmox::UI::message("Purely numeric hostnames are not allowed."); + if ($err || $text =~ m/.example.invalid$/) { + Proxmox::UI::message($err || 'Hostname does not look like a valid fully qualified domain name'); $hostentry->grab_focus(); return; - } - - if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ && - $text =~ m/^([^\.]+)\.(\S+)$/) { - Proxmox::Install::Config::set_hostname($1); - Proxmox::Install::Config::set_domain($2); } else { - Proxmox::UI::message("Hostname does not look like a fully qualified domain name."); - $hostentry->grab_focus(); - return; + Proxmox::Install::Config::set_hostname($hostname); + Proxmox::Install::Config::set_domain($domainname); } # verify ip address @@ -475,8 +502,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(); @@ -609,12 +636,12 @@ sub update_layout { my $lastzonecb; sub update_zonelist { - my ($box, $cc) = @_; + my ($grid, $cc) = @_; my $sel = Proxmox::Install::Config::get_timezone(); # initial default if ($lastzonecb) { $sel = $lastzonecb->get_active_text(); - $box->remove($lastzonecb); + $grid->remove($lastzonecb); } my $cb = $lastzonecb = Gtk3::ComboBoxText->new(); @@ -640,56 +667,49 @@ sub update_zonelist { $cb->set_active($selected_index || 0); $cb->show; - $box->pack_start($cb, 0, 0, 0); + $grid->attach($cb, 1, 1, 1, 1); } sub create_password_view { cleanup_view(); - my $password = Proxmox::Install::Config::get_password(); + my $password = Proxmox::Install::Config::get_root_password('plain'); - my $vbox2 = Gtk3::Box->new('vertical', 0); - $gtk_state->{inbox}->pack_start($vbox2, 1, 0, 0); - my $vbox = Gtk3::Box->new('vertical', 0); - $vbox2->pack_start($vbox, 0, 0, 10); + my $grid = &$create_basic_grid(); + $gtk_state->{inbox}->pack_start($grid, 0, 0, 0); - my $hbox1 = Gtk3::Box->new('horizontal', 0); my $label = Gtk3::Label->new("Password"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox1->pack_start($label, 0, 0, 10); + $grid->attach($label, 0, 0, 1, 1); my $pwe1 = Gtk3::Entry->new(); $pwe1->set_visibility(0); $pwe1->set_text($password) if $password; $pwe1->set_size_request(200, -1); - $hbox1->pack_start($pwe1, 0, 0, 0); + $grid->attach($pwe1, 1, 0, 1, 1); - my $hbox2 = Gtk3::Box->new('horizontal', 0); $label = Gtk3::Label->new("Confirm"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox2->pack_start($label, 0, 0, 10); + $grid->attach($label, 0, 1, 1, 1); my $pwe2 = Gtk3::Entry->new(); $pwe2->set_visibility(0); $pwe2->set_text($password) if $password; $pwe2->set_size_request(200, -1); - $hbox2->pack_start($pwe2, 0, 0, 0); + $grid->attach($pwe2, 1, 1, 1, 1); - my $hbox3 = Gtk3::Box->new('horizontal', 0); $label = Gtk3::Label->new("Email"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox3->pack_start($label, 0, 0, 10); + $label->set_margin_top(10); + $grid->attach($label, 0, 2, 1, 1); + my $eme = Gtk3::Entry->new(); $eme->set_size_request(200, -1); $eme->set_text(Proxmox::Install::Config::get_mailto()); - $hbox3->pack_start($eme, 0, 0, 0); - - - $vbox->pack_start($hbox1, 0, 0, 5); - $vbox->pack_start($hbox2, 0, 0, 5); - $vbox->pack_start($hbox3, 0, 0, 15); + $eme->set_margin_top(10); + $grid->attach($eme, 1, 2, 1, 1); $gtk_state->{inbox}->show_all; @@ -725,7 +745,7 @@ sub create_password_view { return; } - Proxmox::Install::Config::set_password($t1); + Proxmox::Install::Config::set_root_password('plain', $t1); Proxmox::Install::Config::set_mailto($t3); $step_number++; @@ -743,10 +763,8 @@ sub create_country_view { my $locales = $iso_env->{locales}; - my $vbox2 = Gtk3::Box->new('vertical', 0); - $gtk_state->{inbox}->pack_start($vbox2, 1, 0, 0); - my $vbox = Gtk3::Box->new('vertical', 0); - $vbox2->pack_start($vbox, 0, 0, 10); + my $grid = &$create_basic_grid(); + $gtk_state->{inbox}->pack_start($grid, 0, 0, 0); my $w = Gtk3::Entry->new(); $w->set_size_request(200, -1); @@ -757,18 +775,16 @@ sub create_country_view { $c->set_popup_set_width(1); $c->set_inline_completion(1); - my $hbox2 = Gtk3::Box->new('horizontal', 0); my $label = Gtk3::Label->new("Time zone"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox2->pack_start($label, 0, 0, 10); - update_zonelist ($hbox2); + $grid->attach($label, 0, 1, 1, 1); + update_zonelist ($grid); - my $hbox3 = Gtk3::Box->new('horizontal', 0); $label = Gtk3::Label->new("Keyboard Layout"); $label->set_size_request(150, -1); $label->set_xalign(1.0); - $hbox3->pack_start($label, 0, 0, 10); + $grid->attach($label, 0, 2, 1, 1); my $kmapcb = Gtk3::ComboBoxText->new(); $kmapcb->set_size_request (200, -1); @@ -777,7 +793,7 @@ sub create_country_view { } update_layout($kmapcb); - $hbox3->pack_start ($kmapcb, 0, 0, 0); + $grid->attach($kmapcb, 1, 2, 1, 1); $kmapcb->signal_connect ('changed' => sub { my $sel = $kmapcb->get_active_text(); @@ -792,7 +808,7 @@ sub create_country_view { $installer_kmap = $kmap; if (!is_test_mode()) { - syscmd ("setxkbmap $xkmap $xvar"); + syscmd("setxkbmap $xkmap $xvar"); my $kbd_config = qq{ XKBLAYOUT="$xkmap" @@ -814,7 +830,7 @@ sub create_country_view { my $text = $entry->get_text; if (my $cc = $locales->{countryhash}->{lc($text)}) { - update_zonelist($hbox2, $cc); + update_zonelist($grid, $cc); my $kmap = $locales->{country}->{$cc}->{kmap} || 'en-us'; update_layout($kmapcb, $kmap); } @@ -874,17 +890,11 @@ sub create_country_view { $w->set_completion ($c); - my $hbox = Gtk3::Box->new('horizontal', 0); - $label = Gtk3::Label->new("Country"); $label->set_xalign(1.0); $label->set_size_request(150, -1); - $hbox->pack_start($label, 0, 0, 10); - $hbox->pack_start($w, 0, 0, 0); - - $vbox->pack_start($hbox, 0, 0, 5); - $vbox->pack_start($hbox2, 0, 0, 5); - $vbox->pack_start($hbox3, 0, 0, 5); + $grid->attach($label, 0, 0, 1, 1); + $grid->attach($w, 1, 0, 1, 1); my $country = Proxmox::Install::Config::get_country(); if ($country && (my $entry = $locales->{country}->{$country})) { @@ -917,36 +927,30 @@ my $target_hd_label; my $hdoption_first_setup = 1; -my $create_basic_grid = sub { - my $grid = Gtk3::Grid->new(); - $grid->set_visible(1); - $grid->set_column_spacing(10); - $grid->set_row_spacing(10); - $grid->set_hexpand(1); - - $grid->set_margin_start(10); - $grid->set_margin_end(20); - $grid->set_margin_top(5); - $grid->set_margin_bottom(5); - - return $grid; -}; - +# takes an array ref of rows with [$label_text, $widget, $suffix_label] array refs as columns +# $suffix_label is optional my $create_label_widget_grid = sub { my ($labeled_widgets) = @_; my $grid = &$create_basic_grid(); - my $row = 0; - for (my $i = 0; $i < @$labeled_widgets; $i += 2) { - my $widget = @$labeled_widgets[$i+1]; - my $label = Gtk3::Label->new(@$labeled_widgets[$i]); + for (my $row = 0; $row < scalar($labeled_widgets->@*); $row++) { + my ($label_text, $widget, $suffix_label) = $labeled_widgets->[$row]->@*; + + my $label = Gtk3::Label->new($label_text); $label->set_visible(1); $label->set_xalign(1.0); $grid->attach($label, 0, $row, 1, 1); + $widget->set_visible(1); $grid->attach($widget, 1, $row, 1, 1); - $row++; + + if ($suffix_label) { + my $suffix_label = Gtk3::Label->new($suffix_label); + $suffix_label->set_visible(1); + $suffix_label->set_xalign(1.0); + $grid->attach($suffix_label, 2, $row, 1, 1); + } } return $grid; @@ -961,8 +965,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; @@ -1001,7 +1004,7 @@ my sub get_hdsize_spin_button { my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1); $spinbutton_hdsize->set_buffer($hdsize_entry_buffer); $spinbutton_hdsize->set_adjustment($hdsize_size_adj); - $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)"); + $spinbutton_hdsize->set_tooltip_text("only use specified size of the harddisk (rest left unpartitioned)"); return $spinbutton_hdsize; }; @@ -1027,7 +1030,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()); } @@ -1037,8 +1040,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); @@ -1049,7 +1051,7 @@ my $create_raid_disk_grid = sub { } } - push @$disk_labeled_widgets, "Harddisk $i", $disk_selector; + push @$disk_labeled_widgets, ["Harddisk $i", $disk_selector]; } my $clear_all_button = Gtk3::Button->new('_Deselect All'); @@ -1057,8 +1059,7 @@ my $create_raid_disk_grid = sub { $clear_all_button->signal_connect('clicked', sub { my $is_widget = 0; for my $disk_selector (@$disk_labeled_widgets) { - $disk_selector->set_active(0) if $is_widget; - $is_widget ^= 1; + $disk_selector->[1]->set_active(0); } }); $clear_all_button->set_visible(1); @@ -1097,8 +1098,7 @@ my $create_raid_advanced_grid = sub { }); my $ashift = Proxmox::Install::Config::get_zfs_opt('ashift') // 12; $spinbutton_ashift->set_value($ashift); - push @$labeled_widgets, "ashift"; - push @$labeled_widgets, $spinbutton_ashift; + push @$labeled_widgets, ['ashift', $spinbutton_ashift ]; my $combo_compress = Gtk3::ComboBoxText->new(); $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset"); @@ -1112,12 +1112,11 @@ my $create_raid_advanced_grid = sub { my $w = shift; Proxmox::Install::Config::set_zfs_opt('compress', $w->get_active_text()); }); - push @$labeled_widgets, "compress"; - push @$labeled_widgets, $combo_compress; + push @$labeled_widgets, ['compress', $combo_compress]; my $combo_checksum = Gtk3::ComboBoxText->new(); $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset"); - my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"]; + my $csum_opts = ["on", "fletcher4", "sha256"]; foreach my $opt (@$csum_opts) { $combo_checksum->append($opt, $opt); } @@ -1127,8 +1126,7 @@ my $create_raid_advanced_grid = sub { my $w = shift; Proxmox::Install::Config::set_zfs_opt('checksum', $w->get_active_text()); }); - push @$labeled_widgets, "checksum"; - push @$labeled_widgets, $combo_checksum; + push @$labeled_widgets, ['checksum', $combo_checksum]; my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1); $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)"); @@ -1138,16 +1136,31 @@ my $create_raid_advanced_grid = sub { }); my $copies = Proxmox::Install::Config::get_zfs_opt('copies') // 1; $spinbutton_copies->set_value($copies); - push @$labeled_widgets, "copies", $spinbutton_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, 'MiB']; + } - push @$labeled_widgets, "hdsize", $hdsize_btn; + push @$labeled_widgets, ['hdsize', $hdsize_btn, 'GB']; return $create_label_widget_grid->($labeled_widgets);; }; my $create_btrfs_raid_advanced_grid = sub { my ($hdsize_btn) = @_; my $labeled_widgets = []; - push @$labeled_widgets, "hdsize", $hdsize_btn; + push @$labeled_widgets, ['hdsize', $hdsize_btn, 'GB']; return $create_label_widget_grid->($labeled_widgets);; }; @@ -1226,43 +1239,43 @@ sub create_hdoption_view { } my $spinbutton_hdsize_nonraid = get_hdsize_spin_button($hdsize); - push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize_nonraid; + push @$hdsize_labeled_widgets, ['hdsize', $spinbutton_hdsize_nonraid, 'GB']; my $spinbutton_hdsize = $spinbutton_hdsize_nonraid; my $entry_swapsize = Gtk3::Entry->new(); - $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)"); + $entry_swapsize->set_tooltip_text("maximum SWAP size"); $entry_swapsize->signal_connect (key_press_event => \&check_float); my $swapsize = Proxmox::Install::Config::get_swapsize(); $entry_swapsize->set_text($swapsize) if defined($swapsize); - push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize; + push @$hdsize_labeled_widgets, ['swapsize', $entry_swapsize, 'GB']; my $entry_maxroot = Gtk3::Entry->new(); if ($iso_env->{product} eq 'pve') { - $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume"); + $entry_maxroot->set_tooltip_text("maximum size for LVM root volume"); $entry_maxroot->signal_connect (key_press_event => \&check_float); if (my $maxroot = Proxmox::Install::Config::get_maxroot()) { $entry_maxroot->set_text($maxroot); } - push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot; + push @$hdsize_labeled_widgets, ['maxroot', $entry_maxroot, 'GB']; } my $entry_minfree = Gtk3::Entry->new(); - $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)"); + $entry_minfree->set_tooltip_text("minimum free LVM space (required for LVM snapshots)"); $entry_minfree->signal_connect (key_press_event => \&check_float); if (defined(my $minfree = Proxmox::Install::Config::get_minfree())) { $entry_minfree->set_text($minfree); } - push @$hdsize_labeled_widgets, "minfree", $entry_minfree; + push @$hdsize_labeled_widgets, ['minfree', $entry_minfree, 'GB']; my $entry_maxvz; if ($iso_env->{product} eq 'pve') { $entry_maxvz = Gtk3::Entry->new(); - $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume"); + $entry_maxvz->set_tooltip_text("maximum size for LVM data volume"); $entry_maxvz->signal_connect (key_press_event => \&check_float); if (defined(my $maxvz = Proxmox::Install::Config::get_maxvz())) { $entry_maxvz->set_text($maxvz); } - push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz; + push @$hdsize_labeled_widgets, ['maxvz', $entry_maxvz, 'GB']; } my $spinbutton_hdsize_zfs = get_hdsize_spin_button($hdsize); @@ -1311,7 +1324,7 @@ sub create_hdoption_view { $target_hd_label->set_text("Target: $filesys "); $options_stack->set_visible_child_name("raiddisk"); } else { - $target_hd_label->set_text("Target Harddisk: "); + $target_hd_label->set_text("Target Harddisk"); } if ($raid) { @@ -1391,121 +1404,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; +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; -}; - -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"; - } - } - - return ($devlist, $mode); } my $last_hd_selected = 0; @@ -1526,7 +1430,7 @@ sub create_hdsel_view { Proxmox::Install::Config::set_target_hd($devname); } - $target_hd_label = Gtk3::Label->new("Target Harddisk: "); + $target_hd_label = Gtk3::Label->new("Target Harddisk"); $hbox->pack_start($target_hd_label, 0, 0, 0); $target_hd_combo = Gtk3::ComboBoxText->new(); @@ -1565,14 +1469,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; @@ -1582,7 +1488,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"); @@ -1630,6 +1536,7 @@ sub create_extract_view { set_next("_Reboot", sub { app_quit(0); } ); my $autoreboot = Proxmox::Install::Config::get_autoreboot(); + my $autoreboot_seconds = 5; my $success_transform = sub { my ($raw_html, $iso_env) = @_; @@ -1646,6 +1553,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, @@ -1660,6 +1571,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); } @@ -1674,19 +1586,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 { @@ -1709,9 +1619,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; @@ -1731,6 +1642,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;