X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FINotify.pm;h=630e62f2f1afe3812d625a4b4c3f4abb68f162a3;hp=b39748c56310d1cbabf877ef4e697eb3e8828de2;hb=78325766900d362d492d37270e2224840a919129;hpb=b1d7951f61a07529ddc6d24aa63ffe9185c53d98 diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm index b39748c..630e62f 100644 --- a/src/PVE/INotify.pm +++ b/src/PVE/INotify.pm @@ -745,27 +745,71 @@ my $extract_ovs_option = sub { return $v; }; +# config => { +# ifaces => { +# $ifname => { +# exists => BOOL, +# active => BOOL, +# autostart => BOOL, +# priority => INT, +# +# type => "eth" | "bridge" | "bond" | "loopback" | "OVS*" | ... , +# +# families => ["inet", "inet6", ...], +# +# method => "manual" | "static" | "dhcp" | ... , +# address => IP, +# netmask => SUBNET, +# broadcast => IP, +# gateway => IP, +# comments => [ "..." ], +# +# method6 => "manual" | "static" | "dhcp" | ... , +# address6 => IP, +# netmask6 => SUBNET, +# gateway6 => IP, +# comments6 => [ "..." ], +# +# , # like bridge_ports, ovs_* +# +# # extra/unknown options stored by-family: +# options => { ... } +# options6 => { ... } +# } +# }, +# options => [ +# # mappings end up here as well, as we don't need to understand them +# [priority,line] +# ] +# } sub read_etc_network_interfaces { my ($filename, $fh) = @_; + my $proc_net_dev = IO::File->new('/proc/net/dev', 'r'); + my $proc_net_if_inet6 = IO::File->new('/proc/net/if_inet6', 'r'); + return __read_etc_network_interfaces($fh, $proc_net_dev, $proc_net_if_inet6); +} + +sub __read_etc_network_interfaces { + my ($fh, $proc_net_dev, $proc_net_if_inet6) = @_; - my $ifaces = {}; + my $config = {}; + my $ifaces = $config->{ifaces} = {}; + my $options = $config->{options} = []; my $line; - if (my $fd2 = IO::File->new("/proc/net/dev", "r")) { - while (defined ($line = <$fd2>)) { + if ($proc_net_dev) { + while (defined ($line = <$proc_net_dev>)) { if ($line =~ m/^\s*(eth\d+):.*/) { $ifaces->{$1}->{exists} = 1; } } - close($fd2); + close($proc_net_dev); } # we try to keep order inside the file my $priority = 2; # 1 is reserved for lo - my $gateway = 0; - SECTION: while (defined ($line = <$fh>)) { chomp ($line); next if $line =~ m/^\s*#/; @@ -777,19 +821,23 @@ sub read_etc_network_interfaces { $ifaces->{$a}->{autostart} = 1; } - } elsif ($line =~ m/^\s*iface\s+(\S+)\s+inet\s+(\S+)\s*$/) { + } elsif ($line =~ m/^\s*iface\s+(\S+)\s+(inet6?)\s+(\S+)\s*$/) { my $i = $1; - $ifaces->{$i}->{method} = $2; - $ifaces->{$i}->{priority} = $priority++; + my $family = $2; + my $f = { method => $3 }; # by family, merged to $d with a $suffix + (my $suffix = $family) =~ s/^inet//; my $d = $ifaces->{$i}; + $d->{priority} = $priority++ if !$d->{priority}; + push @{$d->{families}}, $family; + while (defined ($line = <$fh>)) { chomp $line; if ($line =~ m/^\s*#(.*?)\s*$/) { # NOTE: we use 'comments' instead of 'comment' to # avoid automatic utf8 conversion - $d->{comments} = '' if !$d->{comments}; - $d->{comments} .= "$1\n"; + $f->{comments} = '' if !$f->{comments}; + $f->{comments} .= "$1\n"; } elsif ($line =~ m/^\s*(?:iface\s |mapping\s |auto\s @@ -801,11 +849,8 @@ sub read_etc_network_interfaces { } elsif ($line =~ m/^\s*((\S+)\s+(.+))$/) { my $option = $1; my ($id, $value) = ($2, $3); - if (($id eq 'address') || ($id eq 'netmask') || ($id eq 'broadcast')) { - $d->{$id} = $value; - } elsif ($id eq 'gateway') { - $d->{$id} = $value; - $gateway = 1; + if (($id eq 'address') || ($id eq 'netmask') || ($id eq 'broadcast') || ($id eq 'gateway')) { + $f->{$id} = $value; } elsif ($id eq 'ovs_type' || $id eq 'ovs_options'|| $id eq 'ovs_bridge' || $id eq 'ovs_bonds' || $id eq 'ovs_ports') { $d->{$id} = $value; @@ -816,7 +861,11 @@ sub read_etc_network_interfaces { $devs->{$p} = 1; } my $str = join (' ', sort keys %{$devs}); - $d->{$id} = $str || ''; + if ($d->{$id}) { + $d->{$id} .= ' ' . $str if $str; + } else { + $d->{$id} = $str || ''; + } } elsif ($id eq 'bridge_stp') { if ($value =~ m/^\s*(on|yes)\s*$/i) { $d->{$id} = 'on'; @@ -840,14 +889,17 @@ sub read_etc_network_interfaces { } $d->{$id} = $value; } else { - push @{$d->{options}}, $option; + push @{$f->{options}}, $option; } } else { last; } } + $d->{"$_$suffix"} = $f->{$_} foreach (keys %$f); last SECTION if !defined($line); redo SECTION; + } elsif ($line =~ /\w/) { + push @$options, [$priority++, $line]; } } @@ -930,38 +982,47 @@ sub read_etc_network_interfaces { } $d->{method} = 'manual' if !$d->{method}; + $d->{method6} = 'manual' if !$d->{method6}; + + $d->{families} ||= ['inet']; } - if (my $fd2 = IO::File->new("/proc/net/if_inet6", "r")) { - while (defined ($line = <$fd2>)) { + if ($proc_net_if_inet6) { + while (defined ($line = <$proc_net_if_inet6>)) { if ($line =~ m/^[a-f0-9]{32}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+(\S+)$/) { $ifaces->{$1}->{active} = 1 if defined($ifaces->{$1}); } } - close ($fd2); + close ($proc_net_if_inet6); } - return $ifaces; + return $config; } sub __interface_to_string { - my ($iface, $d) = @_; + my ($iface, $d, $family, $first_block) = @_; + + (my $suffix = $family) =~ s/^inet//; - return '' if !($d && $d->{method}); + return '' if !($d && $d->{"method$suffix"}); my $raw = ''; - $raw .= "iface $iface inet $d->{method}\n"; - $raw .= "\taddress $d->{address}\n" if $d->{address}; - $raw .= "\tnetmask $d->{netmask}\n" if $d->{netmask}; - $raw .= "\tgateway $d->{gateway}\n" if $d->{gateway}; - $raw .= "\tbroadcast $d->{broadcast}\n" if $d->{broadcast}; + $raw .= "iface $iface $family " . $d->{"method$suffix"} . "\n"; + $raw .= "\taddress " . $d->{"address$suffix"} . "\n" if $d->{"address$suffix"}; + $raw .= "\tnetmask " . $d->{"netmask$suffix"} . "\n" if $d->{"netmask$suffix"}; + $raw .= "\tgateway " . $d->{"gateway$suffix"} . "\n" if $d->{"gateway$suffix"}; + $raw .= "\tbroadcast " . $d->{"broadcast$suffix"} . "\n" if $d->{"broadcast$suffix"}; my $done = { type => 1, priority => 1, method => 1, active => 1, exists => 1, comments => 1, autostart => 1, options => 1, - address => 1, netmask => 1, gateway => 1, broadcast => 1 }; - - if ($d->{type} eq 'bridge') { + address => 1, netmask => 1, gateway => 1, broadcast => 1, + method6 => 1, families => 1, options6 => 1, + address6 => 1, netmask6 => 1, gateway6 => 1, broadcast6 => 1 }; + + if (!$first_block) { + # not printing out options + } elsif ($d->{type} eq 'bridge') { my $ports = $d->{bridge_ports} || 'none'; $raw .= "\tbridge_ports $ports\n"; @@ -1002,7 +1063,6 @@ sub __interface_to_string { $raw .= "\tovs_ports $d->{ovs_ports}\n" if $d->{ovs_ports}; $done->{ovs_ports} = 1; - } elsif ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' || $d->{type} eq 'OVSBond') { @@ -1036,48 +1096,51 @@ sub __interface_to_string { $done->{ovs_bonds} = 1; } - if ($d->{ovs_bridge}) { - $raw = "allow-$d->{ovs_bridge} $iface\n$raw"; - } - $raw .= "\tovs_type $d->{type}\n"; $done->{ovs_type} = 1; if ($d->{ovs_bridge}) { + $raw = "allow-$d->{ovs_bridge} $iface\n$raw"; $raw .= "\tovs_bridge $d->{ovs_bridge}\n"; $done->{ovs_bridge} = 1; } - # fixme: use Data::Dumper; print Dumper($d); } - # print other settings - foreach my $k (keys %$d) { - next if $done->{$k}; - next if !$d->{$k}; - $raw .= "\t$k $d->{$k}\n"; + if ($first_block) { + # print other settings + foreach my $k (keys %$d) { + next if $done->{$k}; + next if !$d->{$k}; + $raw .= "\t$k $d->{$k}\n"; + } } - foreach my $option (@{$d->{options}}) { + foreach my $option (@{$d->{"options$suffix"}}) { $raw .= "\t$option\n"; } # add comments - my $comments = $d->{comments} || ''; + my $comments = $d->{"comments$suffix"} || ''; foreach my $cl (split(/\n/, $comments)) { $raw .= "#$cl\n"; } - if ($d->{autostart}) { - $raw = "auto $iface\n$raw"; - } - $raw .= "\n"; return $raw; } + sub write_etc_network_interfaces { - my ($filename, $fh, $ifaces) = @_; + my ($filename, $fh, $config) = @_; + my $raw = __write_etc_network_interfaces($config); + PVE::Tools::safe_print($filename, $fh, $raw); +} +sub __write_etc_network_interfaces { + my ($config) = @_; + + my $ifaces = $config->{ifaces}; + my @options = @{$config->{options}}; my $used_ports = {}; @@ -1149,7 +1212,19 @@ sub write_etc_network_interfaces { } } - my $raw = "# network interface settings\n"; + my $raw = <<'NETWORKDOC'; +# network interface settings; autogenerated +# Please do NOT modify this file directly, unless you know what +# you're doing. +# +# If you want to manage part of the network configuration manually, +# please utilize the 'source' or 'source-directory' directives to do +# so. +# PVE will preserve these directives, but will NOT its network +# configuration from sourced files, so do not attempt to move any of +# the PVE managed interfaces into external files! + +NETWORKDOC my $printed = {}; @@ -1204,11 +1279,21 @@ sub write_etc_network_interfaces { next if $printed->{$iface}; + if (@options && $options[0]->[0] < $d->{priority}) { + do { + $raw .= (shift @options)->[1] . "\n"; + } while (@options && $options[0]->[0] < $d->{priority}); + $raw .= "\n"; + } + $printed->{$iface} = 1; - $raw .= __interface_to_string($iface, $d); + $raw .= "auto $iface\n" if $d->{autostart}; + my $i = 0; # some options should be printed only once + $raw .= __interface_to_string($iface, $d, $_, !$i++) foreach @{$d->{families}}; } - - PVE::Tools::safe_print($filename, $fh, $raw); + + $raw .= $_->[1] . "\n" foreach @options; + return $raw; } register_file('interfaces', "/etc/network/interfaces",