syscmd("zfs create $zfspoolname/ROOT/$zfsrootvolname") == 0 ||
die "unable to create zfs $zfspoolname/ROOT/$zfsrootvolname volume\n";
- # disable atime during install
- syscmd("zfs set atime=off $zfspoolname") == 0 ||
+ # default to `relatime` on, fast enough for the installer and production
+ syscmd("zfs set atime=on relatime=on $zfspoolname") == 0 ||
die "unable to set zfs properties\n";
my $value = $config_options->{compress};
}
my $hdgb = int($hdsize/(1024*1024));
- die "hardisk '$target_dev' too small (${hdgb}GB)\n" if $hdgb < 8;
+
+ my ($hard_limit, $soft_limit) = (2, 8);
+
+ die "root disk '$target_dev' too small (${hdgb} GB < $hard_limit GB)\n" if $hdgb < $hard_limit;
+ if ($hdgb < $soft_limit) {
+ my $response = display_prompt(
+ "Root disk space ${hdgb} GB is below recommended minimum space of $soft_limit GB,"
+ ." installation might not be successful! Continue?"
+ );
+ die "root disk '$target_dev' too small (${hdgb} GB < $soft_limit GB), and warning not accepted.\n"
+ if $response ne 'ok';
+ }
+
syscmd("sgdisk -Z ${target_dev}");
}
$message .= "or cancel the installation?";
- my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'ok-cancel', $message);
- my $response = $dialog->run();
- $dialog->destroy();
+ my $response = display_prompt($message);
if ($response eq 'ok') {
for my $vg_uuid (keys %$duplicate_vgs) {
syscmd("/sbin/vgcreate $vgname $lvmdev") == 0 ||
die "unable to create volume group '$vgname'\n";
- my $hdgb = int($os_size/(1024*1024));
- my $space = (($hdgb > 128) ? 16 : ($hdgb/8))*1024*1024;
+ my $hdgb = int($os_size / (1024 * 1024));
+
+ # always leave some space at the end to avoid roudning issues with LVM's physical extent (PE)
+ # size of 4 MB.
+ my $space = $hdgb <= 32 ? 4 * 1024 : (($hdgb > 128 ? 16 : $hdgb / 8) * 1024 * 1024);
my $rootsize;
- my $datasize;
+ my $datasize = 0;
if ($setup->{product} eq 'pve') {
- my $maxroot;
+ my $maxroot_mb;
if ($config_options->{maxroot}) {
- $maxroot = $config_options->{maxroot};
+ $maxroot_mb = $config_options->{maxroot} * 1024;
} else {
- $maxroot = 96;
+ $maxroot_mb = 96 * 1024;
}
- $rootsize = (($hdgb > ($maxroot*4)) ? $maxroot : $hdgb/4)*1024*1024;
-
- my $rest = $os_size - $swap_size - $rootsize; # in KB
+ my $rest = $os_size - $swap_size;
+ my $rest_mb = int($rest / 1024);
- my $minfree;
- if (defined($config_options->{minfree})) {
- $minfree = (($config_options->{minfree}*1024*1024) >= $rest ) ? $space :
- $config_options->{minfree}*1024*1024 ;
+ my $rootsize_mb;
+ if ($rest_mb < 12 * 1024) {
+ # no point in wasting space, try to get us actually installed and align down to 4 MB
+ $rootsize_mb = ($rest_mb - 0.1) & ~3;
+ } elsif ($rest_mb < 48 * 1024) {
+ my $masked = int($rest_mb / 2) & ~3; # align down to 4 MB
+ $rootsize_mb = $masked;
} else {
- $minfree = $space;
+ $rootsize_mb = $rest_mb / 4 + 12 * 1024;
+ }
+
+ $rootsize_mb = $maxroot_mb if $rootsize_mb > $maxroot_mb;
+ $rootsize = int($rootsize_mb * 1024);
+
+ $rest -= $rootsize; # in KB
+
+ my $minfree = $space;
+ if (defined(my $cfg_minfree = $config_options->{minfree})) {
+ $minfree = $cfg_minfree * 1024 * 1024 >= $rest ? $space : $cfg_minfree * 1024 * 1024;
}
- $rest = $rest - $minfree;
+ $rest = int($rest - $minfree) & ~0xFFF; # align down to 4 MB boundaries
- if (defined($config_options->{maxvz})) {
- $rest = (($config_options->{maxvz}*1024*1024) <= $rest) ?
- $config_options->{maxvz}*1024*1024 : $rest;
+ if (defined(my $maxvz = $config_options->{maxvz})) {
+ $rest = $maxvz * 1024 * 1024 <= $rest ? $maxvz * 1024 * 1024 : $rest;
}
$datasize = $rest;
} else {
my $minfree = defined($config_options->{minfree}) ? $config_options->{minfree}*1024*1024 : $space;
- $rootsize = $os_size - $minfree - $swap_size; # in KB
+ $rootsize = int($os_size - $minfree - $swap_size); # in KB
+ $rootsize &= ~0xFFF; # align down to 4 MB boundaries
}
if ($swap_size) {
syscmd("/sbin/lvcreate -Wy --yes -L${rootsize}K -nroot $vgname") == 0 ||
die "unable to create root volume\n";
- if ($datasize > 4*1024*1024) {
+ if ($datasize > 4 * 1024 * 1024) {
my $metadatasize = $datasize/100; # default 1% of data
$metadatasize = 1024*1024 if $metadatasize < 1024*1024; # but at least 1G
$metadatasize = 16*1024*1024 if $metadatasize > 16*1024*1024; # but at most 16G
# otherwise the metadata is taken out of $minfree
- $datasize -= 2*$metadatasize;
+ $datasize -= 2 * $metadatasize;
# 1 4MB PE to allow for rounding
- $datasize -= 4*1024;
+ $datasize -= 4 * 1024;
syscmd("/sbin/lvcreate -Wy --yes -L${datasize}K -ndata $vgname") == 0 ||
die "unable to create data volume\n";
syscmd("/sbin/lvconvert --yes --type thin-pool --poolmetadatasize ${metadatasize}K $vgname/data") == 0 ||
die "unable to create data thin-pool\n";
} else {
+ if ($setup->{product} eq 'pve' && !defined($config_options->{maxvz})) {
+ display_message("Skipping auto-creation of LVM thinpool for guest data due to low space.");
+ }
$datadev = undef;
}
my $hdgb = int($hdsize/(1024*1024));
- my $swapsize;
+ my $swapsize_kb;
if (defined($config_options->{swapsize})) {
- $swapsize = $config_options->{swapsize}*1024*1024;
+ $swapsize_kb = $config_options->{swapsize} * 1024 * 1024;
} else {
- my $ss = int ($total_memory / 1024);
- $ss = 4 if $ss < 4;
- $ss = ($hdgb/8) if $ss > ($hdgb/8);
- $ss = 8 if $ss > 8;
- $swapsize = $ss*1024*1024;
+ my $ss = int($total_memory);
+ $ss = 4096 if $ss < 4096 && $hdgb >= 64;
+ $ss = 2048 if $ss < 2048 && $hdgb >= 32;
+ $ss = 1024 if $ss >= 2048 && $hdgb <= 16;
+ $ss = 512 if $ss < 512;
+ $ss = int($hdgb * 128) if $ss > $hdgb * 128;
+ $ss = 8192 if $ss > 8192;
+ $swapsize_kb = int($ss * 1024) & ~0xFFF; # align to 4 MB to avoid all to odd SWAP size
}
- return $swapsize;
+ return $swapsize_kb;
}
my sub chroot_chown {
my $bootloader_err;
eval {
-
-
my $maxper = 0.25;
update_progress(0, 0, $maxper, "cleanup root-disks");
my $logical_bsize = @$hd[4];
my ($size, $osdev, $efidev) =
- partition_bootable_disk($devname, undef, '8300');
+ partition_bootable_disk($devname, $config_options->{hdsize}, '8300');
$rootdev = $osdev if !defined($rootdev); # simply point to first disk
my $by_id = find_stable_path("/dev/disk/by-id", $devname);
push @$bootdevinfo, {
my ($size, $osdev, $efidev) =
partition_bootable_disk($devname, $config_options->{hdsize}, 'BF01');
- zfs_mirror_size_check($disksize, $size) if $disksize;
-
push @$bootdevinfo, {
esp => $efidev,
devname => $devname,
});
syscmd("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 || die "unable to mount tmpfs on $targetdir/tmp\n";
+
+ mkdir "$targetdir/tmp/pkg";
+ syscmd("mount -n --bind '$proxmox_pkgdir' '$targetdir/tmp/pkg'") == 0
+ || die "unable to bind-mount packages on $targetdir/tmp/pkg\n";
syscmd("mount -n -t proc proc $targetdir/proc") == 0 || 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') {
my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
update_progress($count/$pkg_count, 0.5, 0.75, "extracting $deb");
print "extracting: $deb\n";
- syscmd("cp $path $targetdir/tmp/$deb") == 0 ||
- die "installation of package $deb failed\n";
- syscmd("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
- die "installation of package $deb failed\n";
+ syscmd("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/pkg/$deb") == 0
+ || die "installation of package $deb failed\n";
update_progress((++$count)/$pkg_count, 0.5, 0.75);
}
run_command($cmd, sub {
my $line = shift;
if ($line =~ m/Setting up\s+(\S+)/) {
- update_progress((++$count)/$pkg_count, 0.75, 0.95,
- "configuring $1");
+ update_progress((++$count)/$pkg_count, 0.75, 0.95, "configuring $1");
}
});
# create /etc/aliases.db (/etc/aliases is shipped in the base squashfs)
syscmd("chroot $targetdir /usr/bin/newaliases");
- # enable NTP (timedatectl set-ntp true does not work without DBUS)
- syscmd("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
-
unlink "$targetdir/proxmox_install_mode";
# set timezone
my $zfs_snippet = "GRUB_CMDLINE_LINUX=\"\$GRUB_CMDLINE_LINUX root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs\"";
write_config($zfs_snippet, "$targetdir/etc/default/grub.d/zfs.cfg");
- write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs", "$targetdir/etc/kernel/cmdline");
+ write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs\n", "$targetdir/etc/kernel/cmdline");
}
syscmd("umount $targetdir/run");
syscmd("umount $targetdir/mnt/hostrun");
+ syscmd("umount $targetdir/tmp/pkg");
syscmd("umount $targetdir/tmp");
syscmd("umount $targetdir/proc");
syscmd("umount $targetdir/sys/firmware/efi/efivars");
syscmd("umount $targetdir/sys");
+ rmdir("$targetdir/mnt/hostrun");
if ($use_zfs) {
syscmd("zfs umount -a") == 0 ||
syscmd("zfs set mountpoint=/ $zfspoolname/ROOT/$zfsrootvolname") == 0 ||
die "zfs set mountpoint failed\n";
- syscmd("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname") == 0 ||
- die "zfs set bootfs failed\n";
+ syscmd("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname") == 0 ||
+ die "zpool set bootfs failed\n";
syscmd("zpool export $zfspoolname");
}
sub display_message {
my ($msg) = @_;
- my $dialog = Gtk3::MessageDialog->new($window, 'modal',
- 'info', 'ok', $msg);
+ my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'info', 'ok', $msg);
$dialog->run();
$dialog->destroy();
}
sub display_error {
my ($msg) = @_;
- my $dialog = Gtk3::MessageDialog->new($window, 'modal',
- 'error', 'ok', $msg);
+ my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'error', 'ok', $msg);
$dialog->run();
$dialog->destroy();
}
+sub display_prompt {
+ my ($query) = @_;
+
+ my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'ok-cancel', $query);
+ my $response = $dialog->run();
+ $dialog->destroy();
+
+ return $response;
+}
+
my $ipconf_first_view = 1;
sub create_ipconf_view {
my ($devname, $size, $model) = @_;
if ($size && ($size > 0)) {
- $size = int($size/2048); # size in MB, from 512B "sectors"
+ $size = int($size/2048); # size in MiB, from 512B "sectors"
my $text = "$devname (";
if ($size >= 1024) {
- $size = int($size/1024); # size in GB
+ $size = $size/1024; # size in GiB
if ($size >= 1024) {
- $size = int($size/1024); # size in GB
- $text .= "${size}TiB";
+ $size = $size/1024; # size in TiB
+ $text .= sprintf("%.2f", $size) . "TiB";
} else {
- $text .= "${size}GiB";
+ $text .= sprintf("%.2f", $size) . "GiB";
}
} else {
$text .= "${size}MiB";
$text .= ", $model" if $model;
$text .= ")";
+ return $text;
} else {
return $devname;
return $grid;
};
+# only relevant for raid with its multipl diskX to diskY mappings.
+my $get_selected_hdsize = sub {
+ my $hdsize = shift;
+ return $hdsize if defined($hdsize);
+
+ # compute the smallest disk size of the actually selected disks
+ my $hd_count = scalar(@$hds);
+ for (my $i = 0; $i < $hd_count; $i++) {
+ my $cur_hd = $config_options->{"disksel$i"} // next;
+ my $disksize = int(@$cur_hd[2] / (2 * 1024 * 1024.0)); # size in GB
+ $hdsize //= $disksize;
+ $hdsize = $disksize if $disksize < $hdsize;
+ }
+
+ if (my $cfg_hdsize = $config_options->{hdsize}) {
+ # had the dialog open previously and set an even lower size than the disk selection allows
+ $hdsize = $cfg_hdsize if $cfg_hdsize < $hdsize;
+ }
+ return $hdsize // 0; # fall back to zero, e.g., if none is selected hdsize cannot be any size
+};
+
+my sub update_hdsize_adjustment {
+ my ($adjustment, $hdsize) = @_;
+
+ $hdsize = $get_selected_hdsize->($hdsize);
+ # expect that lower = 0 and step increments = 1 still are valid
+ $adjustment->set_upper($hdsize + 1);
+ $adjustment->set_value($hdsize);
+}
+
+my sub create_hdsize_adjustment {
+ my ($hdsize) = @_;
+ $hdsize = $get_selected_hdsize->($hdsize);
+ # params are: initial value, lower, upper, step increment, page increment, page size
+ return Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
+}
+
+my sub get_hdsize_spin_button {
+ my $hdsize = shift;
+
+ my $hdsize_entry_buffer = Gtk3::EntryBuffer->new(undef, 1);
+ my $hdsize_size_adj = create_hdsize_adjustment($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)");
+ return $spinbutton_hdsize;
+};
+
my $create_raid_disk_grid = sub {
+ my ($hdsize_buttons) = @_;
+
my $hd_count = scalar(@$hds);
my $disk_labeled_widgets = [];
for (my $i = 0; $i < $hd_count; $i++) {
$disk_selector->append_text("-- do not use --");
$disk_selector->set_active(0);
$disk_selector->set_visible(1);
+
foreach my $hd (@$hds) {
my ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
$disk_selector->append_text(get_device_desc ($devname, $size, $model));
- $disk_selector->{pve_disk_id} = $i;
- $disk_selector->signal_connect (changed => sub {
- my $w = shift;
- my $diskid = $w->{pve_disk_id};
- my $a = $w->get_active - 1;
- $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
- });
}
+ $disk_selector->{pve_disk_id} = $i;
+ $disk_selector->signal_connect(changed => sub {
+ my $w = shift;
+ my $diskid = $w->{pve_disk_id};
+ my $a = $w->get_active - 1;
+ $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
+ for my $btn (@$hdsize_buttons) {
+ update_hdsize_adjustment($btn->get_adjustment());
+ }
+ });
+
if ($hdoption_first_setup) {
$disk_selector->set_active ($i+1) if $hds->[$i];
} else {
return $vbox;
};
-# shared between different ui parts (e.g., ZFS and "normal" single disk FS)
-my $hdsize_size_adj;
-my $hdsize_entry_buffer;
-
-my $get_hdsize_spinbtn = sub {
- my $hdsize = shift;
-
- $hdsize_entry_buffer //= Gtk3::EntryBuffer->new(undef, 1);
-
- if (defined($hdsize)) {
- $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
- } else {
- die "called get_hdsize_spinbtn with \$hdsize_size_adj not defined but did not pass hdsize!\n"
- if !defined($hdsize_size_adj);
- }
-
- 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)");
- return $spinbutton_hdsize;
-};
-
my $create_raid_advanced_grid = sub {
+ my ($hdsize_btn) = @_;
my $labeled_widgets = [];
my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9, 13, 1);
$spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
my $combo_compress = Gtk3::ComboBoxText->new();
$combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
- # note: gzip / lze not allowed for bootfs vdevs
- my $comp_opts = ["on","off","lzjb","lz4"];
+ my $comp_opts = ["on","off","lzjb","lz4", "zle", "gzip", "zstd"];
foreach my $opt (@$comp_opts) {
$combo_compress->append($opt, $opt);
}
$spinbutton_copies->set_value($config_options->{copies});
push @$labeled_widgets, "copies", $spinbutton_copies;
- push @$labeled_widgets, "hdsize", $get_hdsize_spinbtn->();
+ push @$labeled_widgets, "hdsize", $hdsize_btn;
return $create_label_widget_grid->($labeled_widgets);;
};
-sub create_hdoption_view {
+my $create_btrfs_raid_advanced_grid = sub {
+ my ($hdsize_btn) = @_;
+ my $labeled_widgets = [];
+ push @$labeled_widgets, "hdsize", $hdsize_btn;
+ return $create_label_widget_grid->($labeled_widgets);;
+};
+sub create_hdoption_view {
my $dialog = Gtk3::Dialog->new();
$dialog->set_title("Harddisk options");
# size compute
my $hdsize = 0;
if ( -b $target_hd) {
- $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
+ $hdsize = int(hd_size ($target_hd) / (1024 * 1024.0)); # size in GB
} elsif ($target_hd) {
- $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
+ $hdsize = int((-s $target_hd) / (1024 * 1024 * 1024.0));
}
- my $spinbutton_hdsize = $get_hdsize_spinbtn->($hdsize);
- push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
+ my $spinbutton_hdsize_nonraid = get_hdsize_spin_button($hdsize);
+ push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize_nonraid;
+ my $spinbutton_hdsize = $spinbutton_hdsize_nonraid;
my $entry_swapsize = Gtk3::Entry->new();
$entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
}
+ my $spinbutton_hdsize_zfs = get_hdsize_spin_button($hdsize);
+ my $spinbutton_hdsize_btrfs = get_hdsize_spin_button($hdsize);
+ my $hdsize_buttons = [ $spinbutton_hdsize_zfs, $spinbutton_hdsize_btrfs ];
my $options_stack = Gtk3::Stack->new();
$options_stack->set_visible(1);
$options_stack->set_hexpand(1);
$options_stack->set_vexpand(1);
- $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
+ $options_stack->add_titled(&$create_raid_disk_grid($hdsize_buttons), "raiddisk", "Disk Setup");
$options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
- $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
+ $options_stack->add_titled(&$create_raid_advanced_grid($spinbutton_hdsize_zfs), "raidzfsadvanced", "Advanced Options");
+ $options_stack->add_titled(&$create_btrfs_raid_advanced_grid($spinbutton_hdsize_btrfs), "raidbtrfsadvanced", "Advanced Options");
$options_stack->set_visible_child_name("raiddisk");
my $options_stack_switcher = Gtk3::StackSwitcher->new();
$options_stack_switcher->set_halign('center');
$hw_raid_note->set_markup($msg);
}
$hw_raid_note->set_visible($raid);
- $options_stack_switcher->set_visible($is_zfs);
+ $options_stack_switcher->set_visible($raid);
$options_stack->get_child_by_name("raidzfsadvanced")->set_visible($is_zfs);
+ $options_stack->get_child_by_name("raidbtrfsadvanced")->set_visible(!$is_zfs);
if ($raid) {
$target_hd_label->set_text("Target: $config_options->{filesys} ");
$options_stack->set_visible_child_name("raiddisk");
} else {
$target_hd_label->set_text("Target Harddisk: ");
}
+
+ if ($raid) {
+ $spinbutton_hdsize = $is_zfs ? $spinbutton_hdsize_zfs : $spinbutton_hdsize_btrfs;
+ } else {
+ $spinbutton_hdsize = $spinbutton_hdsize_nonraid;
+ }
+
my (undef, $pref_width) = $dialog->get_preferred_width();
my (undef, $pref_height) = $dialog->get_preferred_height();
$pref_height = 750 if $pref_height > 750;
foreach my $hd (@$hds) {
($disk, $devname, $size, $model, $logical_bsize) = @$hd;
- $target_hd_combo->append_text (get_device_desc($devname, $size, $model));
+ $target_hd_combo->append_text(get_device_desc($devname, $size, $model));
}
my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;