export bond_modes (define with 'our' instead of 'my')
[pve-common.git] / src / PVE / INotify.pm
index b3f45ae..8cc00f8 100644 (file)
@@ -340,7 +340,7 @@ sub register_file {
 sub register_regex {
     my ($dir, $regex, $parser, $writer, $update, %options) = @_;
 
-    die "can't register regex after initify_init" if $inotify;
+    die "can't register regex after inotify_init" if $inotify;
 
     my $uid = "$dir/$regex";
     die "regular expression '$uid' already added :ERROR" if defined ($ccacheregex->{$uid});
@@ -540,7 +540,7 @@ sub read_etc_resolv_conf {
        chomp $line;
        if ($line =~ m/^(search|domain)\s+(\S+)\s*/) {
            $res->{search} = $2;
-       } elsif ($line =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*/) {
+       } elsif ($line =~ m/^\s*nameserver\s+($PVE::Tools::IPRE)\s*/) {
            $nscount++;
            if ($nscount <= 3) {
                $res->{"dns$nscount"} = $1;
@@ -648,9 +648,9 @@ sub write_active_workers {
        my $saved = $task->{saved} ? 1 : 0;
        if ($task->{endtime}) {
            if ($task->{status}) {
-               $raw .= sprintf("$upid $saved %08X $task->{status}\n", $task->{endtime});
+               $raw .= sprintf("%s %s %08X %s\n", $upid, $saved, $task->{endtime}, $task->{status});
            } else {
-               $raw .= sprintf("$upid $saved %08X\n", $task->{endtime});
+               $raw .= sprintf("%s %s %08X\n", $upid, $saved, $task->{endtime});
            }
        } else {
            $raw .= "$upid $saved\n";
@@ -665,7 +665,7 @@ register_file('active', "/var/log/pve/tasks/active",
              \&write_active_workers);
 
 
-my $bond_modes = { 'balance-rr' => 0,
+our $bond_modes = { 'balance-rr' => 0,
                   'active-backup' => 1,
                   'balance-xor' => 2,
                   'broadcast' => 3,
@@ -784,6 +784,13 @@ my $extract_ovs_option = sub {
 # }
 sub read_etc_network_interfaces {
     my ($filename, $fh) = @_;
+    my $proc_net_dev = IO::File->new('/proc/net/dev', 'r');
+    my $active = PVE::Network::get_active_interfaces();
+    return __read_etc_network_interfaces($fh, $proc_net_dev, $active);
+}
+
+sub __read_etc_network_interfaces {
+    my ($fh, $proc_net_dev, $active_ifaces) = @_;
 
     my $config = {};
     my $ifaces = $config->{ifaces} = {};
@@ -791,13 +798,13 @@ sub read_etc_network_interfaces {
 
     my $line;
 
-    if (my $fd2 = IO::File->new("/proc/net/dev", "r")) {
-       while (defined ($line = <$fd2>)) {
-           if ($line =~ m/^\s*(eth\d+):.*/) {
+    if ($proc_net_dev) {
+       while (defined ($line = <$proc_net_dev>)) {
+           if ($line =~ m/^\s*(eth\d+|en[^:.]+):.*/) {
                $ifaces->{$1}->{exists} = 1;
            }
        }
-       close($fd2);
+       close($proc_net_dev);
     }
 
     # we try to keep order inside the file
@@ -820,7 +827,7 @@ sub read_etc_network_interfaces {
            my $f = { method => $3 }; # by family, merged to $d with a $suffix
            (my $suffix = $family) =~ s/^inet//;
 
-           my $d = $ifaces->{$i};
+           my $d = $ifaces->{$i} ||= {};
            $d->{priority} = $priority++ if !$d->{priority};
            push @{$d->{families}}, $family;
 
@@ -867,6 +874,8 @@ sub read_etc_network_interfaces {
                        }
                    } elsif ($id eq 'bridge_fd') {
                        $d->{$id} = $value;
+                   } elsif ($id eq 'bridge_vlan_aware') {
+                       $d->{$id} = 1;
                    } elsif ($id eq 'bond_miimon') {
                        $d->{$id} = $value;
                    } elsif ($id eq 'bond_xmit_hash_policy') {
@@ -896,7 +905,11 @@ sub read_etc_network_interfaces {
        }
     }
 
-
+    foreach my $ifname (@$active_ifaces) {
+       if (my $iface = $ifaces->{$ifname}) {
+           $iface->{active} = 1;
+       }
+    }
 
     if (!$ifaces->{lo}) {
        $ifaces->{lo}->{priority} = 1;
@@ -952,7 +965,15 @@ sub read_etc_network_interfaces {
                $ifaces->{$1}->{exists} = 0;
                $d->{exists} = 0;
            }
-       } elsif ($iface =~ m/^eth\d+$/) {
+       } elsif ($iface =~ m/^(\S+)\.\d+$/) {
+           $d->{type} = 'vlan';
+           if (defined ($ifaces->{$1})) {
+               $d->{exists} = $ifaces->{$1}->{exists};
+           } else {
+               $ifaces->{$1}->{exists} = 0;
+               $d->{exists} = 0;
+           }
+       } elsif ($iface =~ m/^(?:eth\d+|en[^:.]+)$/) {
            if (!$d->{ovs_type}) {
                $d->{type} = 'eth';
            } elsif ($d->{ovs_type} eq 'OVSPort') {
@@ -976,16 +997,33 @@ 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 ($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});
+    # OVS bridges create "allow-$BRIDGE $IFACE" lines which we need to remove
+    # from the {options} hash for them to be removed correctly.
+    @$options = grep {defined($_)} map {
+       my ($pri, $line) = @$_;
+       if ($line =~ /^allow-(\S+)\s+(.*)$/) {
+           my $bridge = $1;
+           my @ports = split(/\s+/, $2);
+           if (defined(my $br = $ifaces->{$bridge})) {
+               # if this port is part of a bridge, remove it
+               my %in_ovs_ports = map {$_=>1} split(/\s+/, $br->{ovs_ports});
+               @ports = grep { not $in_ovs_ports{$_} } @ports;
            }
+           # create the allow line for the remaining ports, or delete if empty
+           if (@ports) {
+               [$pri, "allow-$bridge " . join(' ', @ports)];
+           } else {
+               undef;
+           }
+       } else {
+           # don't modify other lines
+           $_;
        }
-       close ($fd2);
-    }
+    } @$options;
 
     return $config;
 }
@@ -1026,6 +1064,11 @@ sub __interface_to_string {
        $v = defined($d->{bridge_fd}) ? $d->{bridge_fd} : 0;
        $raw .= "\tbridge_fd $v\n";
        $done->{bridge_fd} = 1;
+
+       if( defined($d->{bridge_vlan_aware})) {
+           $raw .= "\tbridge_vlan_aware yes\n";
+       }
+       $done->{bridge_vlan_aware} = 1;
     
     } elsif ($d->{type} eq 'bond') {
 
@@ -1121,8 +1164,14 @@ sub __interface_to_string {
     return $raw;
 }
 
+
 sub write_etc_network_interfaces {
     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}};
@@ -1151,7 +1200,14 @@ sub write_etc_network_interfaces {
            $d->{type} eq 'OVSBond') {
            my $brname = $used_ports->{$iface};
            if (!$brname || !$ifaces->{$brname}) { 
-               delete $ifaces->{$iface}; 
+               if ($iface =~ /^(?:eth|en)/) {
+                   $ifaces->{$iface} = { type => 'eth',
+                                         exists => 1,
+                                         method => 'manual',
+                                         families => ['inet'] };
+               } else {
+                   delete $ifaces->{$iface};
+               }
                next;
            }
            my $bd = $ifaces->{$brname};
@@ -1170,6 +1226,7 @@ sub write_etc_network_interfaces {
                my $n = $ifaces->{$p};
                die "OVS bridge '$iface' - unable to find port '$p'\n"
                    if !$n;
+               $n->{autostart} = 0;
                if ($n->{type} eq 'eth') {
                    $n->{type} = 'OVSPort';
                    $n->{ovs_bridge} = $iface;              
@@ -1214,60 +1271,60 @@ NETWORKDOC
     my $printed = {};
 
     my $if_type_hash = {
-       unknown => 0,
-       loopback => 10,
-       eth => 20,
-       bond => 30,
-       bridge => 40,
+       loopback => 100000,
+       eth => 200000,
+       bond => 300000,
+       bridge => 400000,
    };
 
     my $lookup_type_prio = sub {
        my $iface = shift;
 
-       my $alias = 0;
-       if ($iface =~ m/^(\S+):\d+$/) {
+       my $child = 0;
+       if ($iface =~ m/^(\S+)(\.|:)\d+$/) {
            $iface = $1;
-           $alias = 1;
+           $child = 1;
        }
 
        my $pri;
        if ($iface eq 'lo') {
            $pri = $if_type_hash->{loopback};
-       } elsif ($iface =~ m/^eth\d+$/) {
-           $pri = $if_type_hash->{eth} + $alias;
+       } elsif ($iface =~ m/^(?:eth\d+|en[^:.]+)$/) {
+           $pri = $if_type_hash->{eth} + $child;
        } elsif ($iface =~ m/^bond\d+$/) {
-           $pri = $if_type_hash->{bond} + $alias;
+           $pri = $if_type_hash->{bond} + $child;
        } elsif ($iface =~ m/^vmbr\d+$/) {
-           $pri = $if_type_hash->{bridge} + $alias;
+           $pri = $if_type_hash->{bridge} + $child;
        }
 
-       return $pri || ($if_type_hash->{unknown} + $alias);
+       return $pri;
     };
 
     foreach my $iface (sort {
        my $ref1 = $ifaces->{$a};
        my $ref2 = $ifaces->{$b};
-       my $p1 = &$lookup_type_prio($a);
-       my $p2 = &$lookup_type_prio($b);
+       my $tp1 = &$lookup_type_prio($a);
+       my $tp2 = &$lookup_type_prio($b);
 
-       return $p1 <=> $p2 if $p1 != $p2;
+       # Only recognized types are in relation to each other. If one type
+       # is unknown then only consider the interfaces' priority attributes.
+       $tp1 = $tp2 = 0 if !defined($tp1) || !defined($tp2);
 
-       $p1 = $ref1->{priority} || 100000;
-       $p2 = $ref2->{priority} || 100000;
+       my $p1 = $tp1 + ($ref1->{priority} // 50000);
+       my $p2 = $tp2 + ($ref2->{priority} // 50000);
 
        return $p1 <=> $p2 if $p1 != $p2;
 
        return $a cmp $b;
-                      } keys %$ifaces) {
-
-       my $d = $ifaces->{$iface};
-
+    } keys %$ifaces) {
        next if $printed->{$iface};
 
-       if (@options && $options[0]->[0] < $d->{priority}) {
+       my $d = $ifaces->{$iface};
+       my $pri = $d->{priority} // 0;
+       if (@options && $options[0]->[0] < $pri) {
            do {
                $raw .= (shift @options)->[1] . "\n";
-           } while (@options && $options[0]->[0] < $d->{priority});
+           } while (@options && $options[0]->[0] < $pri);
            $raw .= "\n";
        }
 
@@ -1278,8 +1335,7 @@ NETWORKDOC
     }
 
     $raw .= $_->[1] . "\n" foreach @options;
-
-    PVE::Tools::safe_print($filename, $fh, $raw);
+    return $raw;
 }
 
 register_file('interfaces', "/etc/network/interfaces",