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);
$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);
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 $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}) {
$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);
$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.");
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 {
$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();
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;
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;
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;
};
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());
}
$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);
}
}
- 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');
$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);
});
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");
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);
}
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!)");
});
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');
- push @$labeled_widgets, "hdsize", $hdsize_btn;
+ 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, '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);;
};
}
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);
$dialog->destroy();
}
+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]);
+ }
+}
+
my $last_hd_selected = 0;
sub create_hdsel_view {
set_next(undef, sub {
my $filesys = Proxmox::Install::Config::get_filesys();
if ($filesys =~ m/zfs/) {
+ 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.");
}
$target_hds = [ map { $_->[1] } @$devlist ];
} elsif ($filesys =~ m/btrfs/) {
+ 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 $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,
"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 {
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;