use File::Basename;
use Time::HiRes;
-my $release = '4.2';
+my $release = '4.4';
my $kapi = `uname -r`;
chomp $kapi;
'255.255.255.252' => 30
};
+my $ipv4_reverse_mask = [
+ '0.0.0.0',
+ '128.0.0.0',
+ '192.0.0.0',
+ '224.0.0.0',
+ '240.0.0.0',
+ '248.0.0.0',
+ '252.0.0.0',
+ '254.0.0.0',
+ '255.0.0.0',
+ '255.128.0.0',
+ '255.192.0.0',
+ '255.224.0.0',
+ '255.240.0.0',
+ '255.248.0.0',
+ '255.252.0.0',
+ '255.254.0.0',
+ '255.255.0.0',
+ '255.255.128.0',
+ '255.255.192.0',
+ '255.255.224.0',
+ '255.255.240.0',
+ '255.255.248.0',
+ '255.255.252.0',
+ '255.255.254.0',
+ '255.255.255.0',
+ '255.255.255.128',
+ '255.255.255.192',
+ '255.255.255.224',
+ '255.255.255.240',
+ '255.255.255.248',
+ '255.255.255.252',
+ '255.255.255.254',
+ '255.255.255.255',
+];
+
my ($window, $cmdbox, $inbox, $htmlview);
my ($next, $next_fctn, $target_hd);
my ($progress, $progress_status);
sub zfs_create_swap {
my ($swapsize) = @_;
- syscmd ("zfs create -V ${swapsize}K -b 4K $zfspoolname/swap") == 0 ||
- die "unable to create zfs swap device\n";
+ my $cmd = "zfs create -V ${swapsize}K -b 4K";
- syscmd ("zfs set com.sun:auto-snapshot=false $zfspoolname/swap") == 0 ||
- die "unable to set zfs properties\n";
+ $cmd .= " -o com.sun:auto-snapshot=false";
+
+ # copies for swap does not make sense
+ $cmd .= " -o copies=1";
# reduces memory pressure
- syscmd ("zfs set sync=always $zfspoolname/swap") == 0 ||
- die "unable to set zfs properties\n";
+ $cmd .= " -o sync=always";
- # copies for swap does not make sense
- syscmd ("zfs set copies=1 $zfspoolname/swap") == 0 ||
- die "unable to set zfs properties\n";
+ # cheapest compression to drop zero pages
+ $cmd .= " -o compression=zle";
+
+ # skip log devices
+ $cmd .= " -o logbias=throughput";
+ # only cache metadata in RAM (caching swap content does not make sense)
+ $cmd .= " -o primarycache=metadata";
+ # don't cache anything in L2ARC
+ $cmd .= " -o secondarycache=none";
+
+ $cmd .= " $zfspoolname/swap";
+ syscmd ($cmd) == 0 ||
+ die "unable to create zfs swap device\n";
return "/dev/zvol/$zfspoolname/swap";
}
die "hardisk '$target_dev' too small (${hdsize}GB)\n" if $hdgb < 8;
# 1 - BIOS boot partition (Grub Stage2): first free 1M
- # 2 - EFI ESP: next free 128M
+ # 2 - EFI ESP: next free 256M
# 3 - OS/Data partition: rest, up to $maxhdsize in MB
my $grubbootdev = get_partition_dev($target_dev, 1);
my $pcmd = ['sgdisk'];
my $pnum = 1;
- push @$pcmd, "-n${pnum}::+1M", "-t$pnum:EF02";
+ push @$pcmd, "-n${pnum}:1M:+1M", "-t$pnum:EF02";
$pnum = 2;
- push @$pcmd, "-n${pnum}::+128M", "-t$pnum:EF00";
+ push @$pcmd, "-n${pnum}:2M:+256M", "-t$pnum:EF00";
$pnum = 3;
- push @$pcmd, "-n${pnum}::${restricted_hdsize_mb}", "-t$pnum:$ptype";
+ push @$pcmd, "-n${pnum}:258M:${restricted_hdsize_mb}", "-t$pnum:$ptype";
push @$pcmd, $target_dev;
- my $os_size = $hdsize - 130*1024; # 128M + 1M + up to 1M alignment
+ my $os_size = $hdsize - 258*1024; # 256M + 1M + 1M alignment
syscmd($pcmd) == 0 ||
die "unable to partition harddisk '${target_dev}'\n";
$ss = 8 if $ss > 8;
$swapsize = $ss*1024*1024;
}
+
+ return $swapsize;
}
my $udevadm_trigger_block = sub {
my $ntype = $ipversion == 4 ? 'inet' : 'inet6';
+ my $bridge_port = $ipconf->{ifaces}->{$ipconf->{selected}}->{name};
+
+ $ifaces .= "iface $bridge_port $ntype manual\n";
+
$ifaces .=
- "auto vmbr0\niface vmbr0 $ntype static\n" .
+ "\nauto vmbr0\niface vmbr0 $ntype static\n" .
"\taddress $ipaddress\n" .
"\tnetmask $netmask\n" .
"\tgateway $gateway\n" .
- "\tbridge_ports eth0\n" .
+ "\tbridge_ports $bridge_port\n" .
"\tbridge_stp off\n" .
"\tbridge_fd 0\n";
+ foreach my $iface (sort keys %{$ipconf->{ifaces}}) {
+ my $name = $ipconf->{ifaces}->{$iface}->{name};
+ next if $name eq $bridge_port;
+
+ $ifaces .= "\niface $name $ntype manual\n";
+ }
+
write_config ($ifaces, "$targetdir/etc/network/interfaces");
# configure dns
- my $resolfconf = "search $domain\nnameserver $dnsserver\n";
- write_config ($resolfconf, "$targetdir/etc/resolv.conf");
+ my $resolvconf = "search $domain\nnameserver $dnsserver\n";
+ write_config ($resolvconf, "$targetdir/etc/resolv.conf");
# configure fstab
if ($di->{esp}) {
syscmd ("mount -n $di->{esp} $targetdir/boot/efi") == 0 ||
die "unable to mount $di->{esp}\n";
- syscmd ("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
- die "unable to install the EFI boot loader on '$dev'\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";
+ }
+ }
# 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 ||
sub get_ip_config {
- my $ifconfig = `ifconfig eth0`;
+ my $ifaces = {};
+ my $default;
+
+ my $links = `ip -o l`;
+ foreach my $l (split /\n/,$links) {
+ my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
+ next if !$name || $name eq 'lo';
+
+ my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
+ $driver =~ s!^.*/!!;
+
+ $ifaces->{"$index"} = {
+ name => $name,
+ driver => $driver,
+ flags => $flags,
+ state => $state,
+ mac => $mac,
+ };
+
+ my $addresses = `ip -o a s $name`;
+ foreach my $a (split /\n/,$addresses) {
+ my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
+ next if !$ip;
+
+ my $mask = $prefix;
+
+ if ($family eq 'inet') {
+ next if !$ip =~ /$IPV4RE/;
+ next if $prefix < 8 || $prefix > 32;
+ $mask = @$ipv4_reverse_mask[$prefix];
+ } else {
+ next if !$ip =~ /$IPV6RE/;
+ }
+
+ $default = $index if !$default;
+
+ $ifaces->{"$index"}->{"$family"} = {
+ mask => $mask,
+ addr => $ip,
+ };
+ }
+ }
- my ($addr) = $ifconfig =~ m/inet addr:(\S*)/m;
- my ($mask) = $ifconfig =~ m/Mask:(\S*)/m;
- my $route = `route -n`;
- my ($gateway) = $route =~ m/^0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)\s+/m;
+ my $route = `ip route`;
+ my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
my $resolvconf = `cat /etc/resolv.conf`;
my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
return {
- addr => $addr,
- mask => $mask,
+ default => $default,
+ ifaces => $ifaces,
gateway => $gateway,
dnsserver => $dnsserver,
domain => $domain,
$dialog->destroy();
}
+my $ipconf_first_view = 1;
+
sub create_ipconf_view {
cleanup_view ();
my $vbox2 = Gtk3::VBox->new (0, 0);
$hbox->add ($vbox2);
- my $addr = $ipconf->{addr} || '192.168.100.2';
- my $mask = $ipconf->{mask} || '255.255.255.0';
+ my $ipbox;
+ ($ipbox, $ipconf_entry_addr) =
+ create_text_input ("192.168.100.2", 'IP Address:');
+
+ my $maskbox;
+ ($maskbox, $ipconf_entry_mask) =
+ create_text_input ("255.255.255.0", 'Netmask:');
+
+ my $device_cb = Gtk3::ComboBoxText->new();
+ $device_cb->set_active(0);
+ $device_cb->set_visible(1);
+
+ my $get_device_desc = sub {
+ my $iface = shift;
+ return "$iface->{name} - $iface->{mac} ($iface->{driver})";
+ };
+
+ my $device_active_map = {};
+
+ my $device_change_handler = sub {
+ my $current = shift;
+ $ipconf->{selected} = $device_active_map->{$current->get_active()};
+ my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
+ $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
+ if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
+ $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
+ if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
+ };
+
+ my $i = 0;
+ foreach my $index (sort keys %{$ipconf->{ifaces}}) {
+ $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
+ $device_active_map->{$i} = $index;
+ if ($ipconf_first_view && $index == $ipconf->{default}) {
+ $device_cb->set_active($i);
+ &$device_change_handler($device_cb);
+ $ipconf_first_view = 0;
+ }
+ $device_cb->signal_connect ('changed' => $device_change_handler);
+ $i++;
+ }
+
+ $device_cb->set_active(0)
+ if !($ipconf->{selected});
+
+ my $devicebox = Gtk3::HBox->new (0, 0);
+ my $label = Gtk3::Label->new ("Management Interface:");
+ $label->set_size_request (150, -1);
+ $label->set_alignment (1, 0.5);
+ $devicebox->pack_start ($label, 0, 0, 10);
+ $devicebox->pack_start ($device_cb, 0, 0, 0);
+
+ $vbox2->pack_start ($devicebox, 0, 0, 2);
my $hn = $ipconf->{domain} ? "pve.$ipconf->{domain}" : 'pve.example.invalid';
create_text_input ($hn, 'Hostname (FQDN):');
$vbox2->pack_start ($hostbox, 0, 0, 2);
- my $ipbox;
- ($ipbox, $ipconf_entry_addr) =
- create_text_input ($addr, 'IP Address:');
$vbox2->pack_start ($ipbox, 0, 0, 2);
- my $maskbox;
- ($maskbox, $ipconf_entry_mask) =
- create_text_input ($mask, 'Netmask:');
$vbox2->pack_start ($maskbox, 0, 0, 2);
$gateway = $ipconf->{gateway} || '192.168.100.1';
my $t3 = $eme->get_text;
if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
- display_message ("E-Mail does not look like a vaild address" .
+ display_message ("E-Mail does not look like a valid address" .
" (user\@domain.tld)");
$eme->grab_focus();
return;
my $create_raid_advanced_grid = sub {
my $labeled_widgets = [];
- my $entry_ashift = Gtk3::Entry->new();
- $entry_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
- $entry_ashift->signal_connect (key_press_event => \&check_int);
- $entry_ashift->signal_connect (changed => sub {
- my ($entry) = @_;
-
- my $text = $entry->get_text();
- delete $config_options->{ashift} if !defined($text);
-
- $text =~ m/^\s*(\d+)\s*$/;
- $config_options->{ashift} = $1;
+ 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)");
+ $spinbutton_ashift->signal_connect ("value-changed" => sub {
+ my $w = shift;
+ $config_options->{ashift} = $w->get_value_as_int();
});
$config_options->{ashift} = 12 if ! defined($config_options->{ashift});
- $entry_ashift->set_text($config_options->{ashift});
+ $spinbutton_ashift->set_value($config_options->{ashift});
push @$labeled_widgets, "ashift";
- push @$labeled_widgets, $entry_ashift;
+ push @$labeled_widgets, $spinbutton_ashift;
my $combo_compress = Gtk3::ComboBoxText->new();
$combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
$spinbutton_copies->signal_connect ("value-changed" => sub {
my $w = shift;
$config_options->{copies} = $w->get_value_as_int();
- warn "changed: '$config_options->{copies}'\n";
});
$config_options->{copies} = 1 if !defined($config_options->{copies});
$spinbutton_copies->set_value($config_options->{copies});
push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
my $entry_minfree = Gtk3::Entry->new();
- $entry_minfree->set_tooltip_text("minumum free LVM space (GB, required for LVM snapshots)");
+ $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
$entry_minfree->signal_connect (key_press_event => \&check_float);
$entry_minfree->set_text($config_options->{minfree}) if $config_options->{minfree};
push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
my $dev_name_hash = {};
my $devlist = [];
- for (my $i = 0; $i < 8; $i++) {
+ for (my $i = 0; $i < @$hds; $i++) {
if (my $hd = $config_options->{"disksel$i"}) {
my ($disk, $devname, $size, $model) = @$hd;
die "device '$devname' is used more than once\n"
$ipconf = get_ip_config ();
-$country = detect_country() if $ipconf->{addr} || $opt_testmode;;
+$country = detect_country() if $ipconf->{default} || $opt_testmode;;
# read country, kmap and timezone infos
$cmap = read_cmap ();