]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/INotify.pm
INotify: allow vlan tagged bridge interface on non-vlanaware bridge
[pve-common.git] / src / PVE / INotify.pm
index b6600412293d9cdb85c9bc35a6cff030e11b47d9..1a528fa356bc0f7207dfeb4f816487ca596b7d4e 100644 (file)
@@ -84,15 +84,15 @@ sub ccache_info {
                    $cp->{$k} = $v;
                }
                $ccache->{$filename} = $cp;
-           } 
+           }
            return ($ccache->{$filename}, $filename);
        }
     }
+
     $filename = $ccachemap->{$filename} if defined ($ccachemap->{$filename});
 
     die "file '$filename' not added :ERROR" if !defined ($ccache->{$filename});
-   
+
     return ($ccache->{$filename}, $filename);
 }
 
@@ -135,7 +135,7 @@ sub write_file {
     if (!rename($tmpname, $realname)) {
        my $msg = "close (rename) atomic file '$filename' failed: $!\n";
        unlink $tmpname;
-       die $msg;       
+       die $msg;
     }
 
     my $diff;
@@ -168,7 +168,7 @@ sub update_file {
     my $code = sub {
 
        $fd = IO::File->new ($filename, "r");
-       
+
        my $new = &$update($filename, $fd, $data, @args);
 
        if (defined($new)) {
@@ -216,9 +216,9 @@ sub read_file {
     my $parser;
 
     my ($ccinfo, $filename) = ccache_info($fileid);
-     
+
     $parser = $ccinfo->{parser};
+
     my $fd;
     my $shadow;
 
@@ -238,7 +238,7 @@ sub read_file {
 
     if (!$fd) {
        $ccinfo->{version} = undef;
-       $ccinfo->{data} = undef; 
+       $ccinfo->{data} = undef;
        $ccinfo->{diff} = undef;
        return undef if !$acp;
     }
@@ -260,7 +260,7 @@ sub read_file {
            $ret->{data} = $ccinfo->{data};
        }
        $ret->{changes} = $ccinfo->{diff};
-       
+
        return $full ? $ret : $ret->{data};
     }
 
@@ -293,7 +293,7 @@ sub read_file {
     $ret->{changes} = $ccinfo->{diff};
 
     return $full ? $ret : $ret->{data};
-}    
+}
 
 sub parse_ccache_options {
     my ($ccinfo, %options) = @_;
@@ -312,7 +312,7 @@ sub parse_ccache_options {
            # noclone flag for large read-only data chunks like aplinfo
            $ccinfo->{$opt} = $v;
        } elsif ($opt eq 'always_call_parser') {
-           # when set, we call parser even when the file does not exists.
+           # when set, we call parser even when the file does not exist.
            # this allows the parser to return some default
            $ccinfo->{$opt} = $v;
        } else {
@@ -337,7 +337,7 @@ sub register_file {
     $ccinfo->{update} = $update;
 
     parse_ccache_options($ccinfo, %options);
-    
+
     if ($options{shadow}) {
        $shadowfiles->{$filename} = $options{shadow};
     }
@@ -353,7 +353,7 @@ sub register_regex {
 
     my $uid = "$dir/$regex";
     die "regular expression '$uid' already added :ERROR" if defined ($ccacheregex->{$uid});
+
     my $ccinfo = {};
 
     $ccinfo->{dir} = $dir;
@@ -417,7 +417,7 @@ sub inotify_init {
 
     foreach my $uid (keys %$ccacheregex) {
        my $ccinfo = $ccacheregex->{$uid};
-       $dirhash->{$ccinfo->{dir}}->{_regex} = 1;       
+       $dirhash->{$ccinfo->{dir}}->{_regex} = 1;
     }
 
     $inotify_pid = $$;
@@ -447,7 +447,7 @@ sub inotify_init {
                syslog ('err', "got 'unmount' event on '$name' - disabling inotify");
                $inotify = undef;
            }
-           if ($e->IN_IGNORED) { 
+           if ($e->IN_IGNORED) {
                syslog ('err', "got 'ignored' event on '$name' - disabling inotify");
                $inotify = undef;
            }
@@ -480,7 +480,7 @@ sub inotify_init {
                    next if $dir ne $ccinfo->{dir};
                    my $re = $ccinfo->{regex};
                    if (my $fd = IO::Dir->new ($dir)) {
-                       while (defined(my $de = $fd->read)) { 
+                       while (defined(my $de = $fd->read)) {
                            if ($de =~ m/^$re$/) {
                                my $fn = "$dir/$de";
                                $versions->{$fn}++; # init with version
@@ -535,8 +535,8 @@ sub write_etc_hostname {
     return $hostname;
 }
 
-register_file('hostname', "/etc/hostname",  
-             \&read_etc_hostname, 
+register_file('hostname', "/etc/hostname",
+             \&read_etc_hostname,
              \&write_etc_hostname);
 
 sub read_etc_hosts {
@@ -629,12 +629,12 @@ sub update_etc_resolv_conf {
        next if $line =~ m/^(search|domain|nameserver)\s+/;
        $data .= $line
     }
-    
+
     return $data;
 }
 
-register_file('resolvconf', "/etc/resolv.conf", 
-             \&read_etc_resolv_conf, undef, 
+register_file('resolvconf', "/etc/resolv.conf",
+             \&read_etc_resolv_conf, undef,
              \&update_etc_resolv_conf);
 
 sub read_etc_timezone {
@@ -664,8 +664,8 @@ sub write_etc_timezone {
 
 }
 
-register_file('timezone', "/etc/timezone", 
-             \&read_etc_timezone, 
+register_file('timezone', "/etc/timezone",
+             \&read_etc_timezone,
              \&write_etc_timezone);
 
 sub read_active_workers {
@@ -673,7 +673,7 @@ sub read_active_workers {
 
     return [] if !$fh;
 
-    my $res = []; 
+    my $res = [];
     while (defined (my $line = <$fh>)) {
        if ($line =~ m/^(\S+)\s(0|1)(\s([0-9A-Za-z]{8})(\s(\s*\S.*))?)?$/) {
            my $upid = $1;
@@ -717,7 +717,7 @@ sub write_active_workers {
     PVE::Tools::safe_print($filename, $fh, $raw) if $raw;
 }
 
-register_file('active', "/var/log/pve/tasks/active", 
+register_file('active', "/var/log/pve/tasks/active",
              \&read_active_workers,
              \&write_active_workers);
 
@@ -735,7 +735,7 @@ my $ovs_bond_modes = {
     'active-backup' => 1,
     'balance-slb' => 1,
     'lacp-balance-slb' => 1,
-    'lacp-balance-tcp' => 1, 
+    'lacp-balance-tcp' => 1,
 };
 
 #sub get_bond_modes {
@@ -896,14 +896,15 @@ sub __read_etc_network_interfaces {
     }
 
     # we try to keep order inside the file
-    my $priority = 2; # 1 is reserved for lo 
+    my $priority = 2; # 1 is reserved for lo
 
     SECTION: while (defined ($line = <$fh>)) {
        chomp ($line);
        next if $line =~ m/^\s*#/;
-       if ($line =~ m/^\s*auto\s+(.*)$/) {
-           my @aa = split (/\s+/, $1);
+       next if $line =~ m/^\s*(allow-hotplug)\s+(.*)$/;
+
+       if ($line =~ m/^\s*(auto|allow-ovs)\s+(.*)$/) {
+           my @aa = split (/\s+/, $2);
 
            foreach my $a (@aa) {
                $ifaces->{$a}->{autostart} = 1;
@@ -955,6 +956,7 @@ sub __read_etc_network_interfaces {
                        'bridge-multicast-flood' => 1,
                        'bond_miimon' => 1,
                        'bond_xmit_hash_policy' => 1,
+                       'uplink-id' => 1,
                        'vlan-protocol' => 1,
                        'vxlan-id' => 1,
                        'vxlan-svcnodeip' => 1,
@@ -1106,7 +1108,7 @@ sub __read_etc_network_interfaces {
 
        # map address and netmask to cidr
        if ($d->{address}) {
-           if ($d->{netmask} =~ m/^\d+$/) { # e.g. netmask 20
+           if ($d->{netmask} && $d->{netmask} =~ m/^\d+$/) { # e.g. netmask 20
                $d->{cidr} = $d->{address} . "/" . $d->{netmask};
            } elsif ($d->{netmask} &&
                     (my $cidr = PVE::JSONSchema::get_netmask_bits($d->{netmask}))) { # e.g. netmask 255.255.255.0
@@ -1141,7 +1143,9 @@ sub __read_etc_network_interfaces {
     # from the {options} hash for them to be removed correctly.
     @$options = grep {defined($_)} map {
        my ($pri, $line) = @$_;
-       if ($line =~ /^allow-(\S+)\s+(.*)$/) {
+       if ($line =~ /^allow-ovs\s+(.*)$/) {
+           undef;
+       } elsif ($line =~ /^allow-(\S+)\s+(.*)$/) {
            my $bridge = $1;
            my @ports = split(/\s+/, $2);
            if (defined(my $br = $ifaces->{$bridge})) {
@@ -1183,7 +1187,7 @@ sub __interface_to_string {
                 comments => 1, autostart => 1, options => 1,
                 address => 1, netmask => 1, gateway => 1, broadcast => 1,
                 method6 => 1, families => 1, options6 => 1,
-                address6 => 1, netmask6 => 1, gateway6 => 1, broadcast6 => 1 };
+                address6 => 1, netmask6 => 1, gateway6 => 1, broadcast6 => 1, 'uplink-id' => 1 };
 
     if (!$first_block) {
        # not printing out options
@@ -1209,7 +1213,7 @@ sub __interface_to_string {
        }
        $done->{bridge_vlan_aware} = 1;
        $done->{bridge_vids} = 1;
-    
+
     } elsif ($d->{type} eq 'bond') {
 
        $d->{slaves} =~ s/[;,\s]+/ /g;
@@ -1231,9 +1235,9 @@ sub __interface_to_string {
        }
        $done->{'bond_xmit_hash_policy'} = 1;
     } elsif ($d->{type} eq 'vlan') {
-       die "$iface: wrong vlan-protocol $d->{'vlan-protocol'}\n" 
+       die "$iface: wrong vlan-protocol $d->{'vlan-protocol'}\n"
            if $d->{'vlan-protocol'} && $d->{'vlan-protocol'} ne '802.1ad' && $d->{'vlan-protocol'} ne '802.1q';
-       
+
     } elsif ($d->{type} eq 'vxlan') {
 
        foreach my $k (qw(vxlan-id vxlan-svcnodeip vxlan-physdev vxlan-local-tunnelip)) {
@@ -1253,6 +1257,7 @@ sub __interface_to_string {
        $done->{ovs_type} = 1;
 
        $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') {
@@ -1290,15 +1295,9 @@ sub __interface_to_string {
        $raw .= "\tovs_type $d->{type}\n";
        $done->{ovs_type} = 1;
 
-       if ($d->{ovs_bridge}) {
-
-           if ($ifupdown2) {
-               $raw = "auto $iface\n$raw";
-           } else {
-               $raw = "allow-$d->{ovs_bridge} $iface\n$raw";
-           }
-
-           $raw .= "\tovs_bridge $d->{ovs_bridge}\n";
+       if (my $bridge = $d->{ovs_bridge}) {
+           $raw = "allow-$bridge $iface\n$raw";
+           $raw .= "\tovs_bridge $bridge\n";
            $done->{ovs_bridge} = 1;
        }
     }
@@ -1475,9 +1474,7 @@ sub __write_etc_network_interfaces {
            die "vlan '$iface' - unable to find parent '$p'\n"
                if !$n;
 
-           if ($n->{type} eq 'bridge' && !$n->{bridge_vlan_aware}) {
-               die "vlan '$iface' - bridge vlan aware is not enabled on parent '$p'\n";
-           } elsif ($n->{type} ne 'eth' && $n->{type} ne 'bridge' && $n->{type} ne 'bond' && $n->{type} ne 'vlan') {
+           if ($n->{type} ne 'eth' && $n->{type} ne 'bridge' && $n->{type} ne 'bond' && $n->{type} ne 'vlan') {
                die "vlan '$iface' - wrong interface type on parent '$p' " .
                    "('$n->{type}' != 'eth|bond|bridge|vlan' )\n";
            }
@@ -1487,6 +1484,21 @@ sub __write_etc_network_interfaces {
        }
     }
 
+    # check uplink
+    my $uplinks = {};
+    foreach my $iface (keys %$ifaces) {
+       my $d = $ifaces->{$iface};
+       if (my $uplinkid = $d->{'uplink-id'}) {
+           die "iface '$iface' - uplink-id $uplinkid is only allowed on physical and linux bond interfaces\n"
+               if $d->{type} ne 'eth' && $d->{type} ne 'bond';
+
+           die "iface '$iface' - uplink-id $uplinkid is already assigned on '$uplinks->{$uplinkid}'\n"
+               if $uplinks->{$uplinkid};
+
+           $uplinks->{$uplinkid} = $iface;
+       }
+    }
+
     # check bridgeport option
     my $bridgeports = {};
     my $bridges = {};
@@ -1498,6 +1510,10 @@ sub __write_etc_network_interfaces {
                my $n = $ifaces->{$p};
                die "bridge '$iface' - unable to find bridge port '$p'\n"
                    if !$n;
+               die "iface $p - ip address can't be set on interface if bridged in $iface\n" 
+                   if ($n->{method} eq 'static' && $n->{address} ne '0.0.0.0') || 
+                       ($n->{method6} eq 'static' && $n->{address} ne "\:\:");
+
                &$check_mtu($ifaces, $iface, $p);
                $bridgeports->{$p} = $iface;
            }
@@ -1576,7 +1592,6 @@ NETWORKDOC
        return $a cmp $b;
     } keys %$ifaces) {
        next if $printed->{$iface};
-
        my $d = $ifaces->{$iface};
        my $pri = $d->{priority} // 0;
        if (@options && $options[0]->[0] < $pri) {
@@ -1587,7 +1602,15 @@ NETWORKDOC
        }
 
        $printed->{$iface} = 1;
-       $raw .= "auto $iface\n" if $d->{autostart};
+       if ($d->{autostart}) {
+           if ($d->{type} eq 'OVSBridge') {
+               # cannot use 'auto' for OVS, would add race with systemd ifup@.service
+               $raw .= "allow-ovs $iface\n";
+           } else {
+               $raw .= "auto $iface\n";
+           }
+       }
+
        my $i = 0; # some options should be printed only once
        $raw .= __interface_to_string($iface, $d, $_, !$i++, $ifupdown2) foreach @{$d->{families}};
     }
@@ -1613,7 +1636,7 @@ sub read_iscsi_initiatorname {
     return 'undefined';
 }
 
-register_file('initiatorname', "/etc/iscsi/initiatorname.iscsi",  
+register_file('initiatorname', "/etc/iscsi/initiatorname.iscsi",
              \&read_iscsi_initiatorname);
 
 sub read_apt_auth {
@@ -1625,7 +1648,7 @@ sub read_apt_auth {
 
     $raw =~ s/^\s+//;
 
+
     my @tokens = split(/\s+/, $raw);
 
     my $data = {};
@@ -1666,7 +1689,7 @@ sub write_apt_auth {
     my $raw = &$format_apt_auth_data($data);
 
     die "write failed: $!" unless print $fh "$raw\n";
-   
+
     return $data;
 }
 
@@ -1682,7 +1705,7 @@ sub update_apt_auth {
     return &$format_apt_auth_data($orig);
 }
 
-register_file('apt-auth', "/etc/apt/auth.conf",  
+register_file('apt-auth', "/etc/apt/auth.conf",
              \&read_apt_auth, \&write_apt_auth,
              \&update_apt_auth, perm => 0640);