]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/INotify.pm
bump version to 8.2.1
[pve-common.git] / src / PVE / INotify.pm
index 0d82a522b63f105ad271eaaae7490d40ab6529e4..8a4a810f51fbbfa5905a4973841b5bb037f4df9f 100644 (file)
@@ -22,10 +22,11 @@ use PVE::Network;
 use PVE::ProcFSTools;
 use PVE::SafeSyslog;
 use PVE::Tools;
+use PVE::RESTEnvironment qw(log_warn);
 
 use base 'Exporter';
 
-our @EXPORT_OK = qw(read_file write_file register_file);
+our @EXPORT_OK = qw(read_file write_file register_file nodename);
 
 my $ccache;
 my $ccachemap;
@@ -500,13 +501,10 @@ sub inotify_init {
 }
 
 my $cached_nodename;
-
 sub nodename {
-
     return $cached_nodename if $cached_nodename;
 
     my ($sysname, $nodename) = POSIX::uname();
-
     $nodename =~ s/\..*$//; # strip domain part, if any
 
     die "unable to read node name\n" if !$nodename;
@@ -884,7 +882,7 @@ sub __read_etc_network_interfaces {
        'bridge-fd' => 'bridge_fd',
        'bridge-stp' => 'bridge_stp',
        'bridge-ports' => 'bridge_ports',
-       'bridge-vids' => 'bridge_vids'
+       'bridge-vids' => 'bridge_vids',
     };
 
     my $line;
@@ -914,23 +912,29 @@ sub __read_etc_network_interfaces {
            # FIXME: handle those differently? auto makes it required on-boot, vs. best-effort
            $ifaces->{$_}->{autostart} = 1 for split (/\s+/, $2);
 
-       } elsif ($line =~ m/^\s*iface\s+(\S+)\s+(inet6?)\s+(\S+)\s*$/) {
+       } elsif ($line =~ m/^\s*iface\s+(\S+)(?:\s+(inet6?)\s+(\S+))?\s*$/) {
            my $i = $1;
            my $family = $2;
            my $f = { method => $3 }; # by family, merged to $d with a $suffix
-           (my $suffix = $family) =~ s/^inet//;
+           my $suffix = $family;
+           $suffix =~ s/^inet// if defined $suffix;
 
            my $d = $ifaces->{$i} ||= {};
            $d->{priority} = $priority++ if !$d->{priority};
+
+           # $family may be undef, an undef family means we have a stanza
+           # without an `inet` or `inet6` section
            push @{$d->{families}}, $family;
 
+
            while (defined ($line = <$fh>)) {
                $line =~ s/\s+$//; # drop trailing whitespaces
 
                if ($line =~ m/^\s*#(.*?)\s*$/) {
-                   $f->{comments} = '' if !$f->{comments};
+                   my $pushto = defined($suffix) ? $f : $d;
+                   $pushto->{comments} = '' if !$pushto->{comments};
                    my $comment = decode('UTF-8', $1);
-                   $f->{comments} .= "$comment\n";
+                   $pushto->{comments} .= "$comment\n";
                } elsif ($line =~ m/^\s*(?:(?:iface|mapping|auto|source|source-directory)\s|allow-)/) {
                    last;
                } elsif ($line =~ m/^\s*((\S+)\s+(.+))$/) {
@@ -953,6 +957,7 @@ sub __read_etc_network_interfaces {
                        'bridge-arp-nd-suppress' => 1,
                        'bridge-unicast-flood' => 1,
                        'bridge-multicast-flood' => 1,
+                       'bridge-disable-mac-learning' => 1,
                        'bond_miimon' => 1,
                        'bond_xmit_hash_policy' => 1,
                        'bond-primary' => 1,
@@ -968,7 +973,17 @@ sub __read_etc_network_interfaces {
                    };
 
                    if ($id eq 'address' || $id eq 'netmask' || $id eq 'broadcast' || $id eq 'gateway') {
-                       $f->{$id} = $value;
+                       if (defined($suffix)) {
+                           $d->{$id.$suffix} = $value;
+                       } elsif ($id ne 'netmask') {
+                           if ($value =~ /:/) {
+                               $d->{$id.'6'} = $value;
+                           } else {
+                               $d->{$id} = $value;
+                           }
+                       } else {
+                           $d->{$id} = $value;
+                       }
                    } elsif ($simple_options->{$id}) {
                        $d->{$id} = $value;
                    } elsif ($id eq 'slaves' || $id eq 'bridge_ports') {
@@ -1003,13 +1018,16 @@ sub __read_etc_network_interfaces {
                    } elsif ($id eq 'vxlan-remoteip') {
                        push @{$d->{$id}}, $value;
                    } else {
-                       push @{$f->{options}}, $option;
+                       my $pushto = defined($suffix) ? $f : $d;
+                       push @{$pushto->{options}}, $option;
                    }
                } else {
                    last;
                }
            }
-           $d->{"$_$suffix"} = $f->{$_} for keys $f->%*;
+           if (defined($suffix)) {
+               $d->{"$_$suffix"} = $f->{$_} for keys $f->%*;
+           }
            last SECTION if !defined($line);
            redo SECTION;
        } elsif ($line =~ /\w/) {
@@ -1032,10 +1050,20 @@ sub __read_etc_network_interfaces {
        };
     }
 
-    foreach my $iface (keys %$ifaces) {
+    foreach my $iface (sort keys %$ifaces) {
        my $d = $ifaces->{$iface};
        $d->{type} = 'unknown';
-       if ($iface =~ m/^bond\d+$/) {
+       if (defined $d->{'bridge_ports'}) {
+           $d->{type} = 'bridge';
+           if (!defined ($d->{bridge_stp})) {
+               $d->{bridge_stp} = 'off';
+           }
+           if (!defined($d->{bridge_fd}) && $d->{bridge_stp} eq 'off') {
+               $d->{bridge_fd} = 0;
+           }
+       } elsif ($d->{ovs_type} && $d->{ovs_type} eq 'OVSBridge') {
+           $d->{type} = $d->{ovs_type};
+       } elsif ($iface =~ m/^bond\d+$/) {
            if (!$d->{ovs_type}) {
                $d->{type} = 'bond';
            } elsif ($d->{ovs_type} eq 'OVSBond') {
@@ -1055,18 +1083,6 @@ sub __read_etc_network_interfaces {
                my $tag = &$extract_ovs_option($d, 'tag');
                $d->{ovs_tag} = $tag if defined($tag);
            }
-       } elsif ($iface =~ m/^vmbr\d+$/) {
-           if (!$d->{ovs_type}) {
-               $d->{type} = 'bridge';
-               if (!defined ($d->{bridge_stp})) {
-                   $d->{bridge_stp} = 'off';
-               }
-               if (!defined($d->{bridge_fd}) && $d->{bridge_stp} eq 'off') {
-                   $d->{bridge_fd} = 0;
-               }
-           } elsif ($d->{ovs_type} eq 'OVSBridge') {
-               $d->{type} = $d->{ovs_type};
-           }
        } elsif ($iface =~ m/^(\S+):\d+$/) {
            $d->{type} = 'alias';
            if (defined ($ifaces->{$1})) {
@@ -1075,16 +1091,27 @@ sub __read_etc_network_interfaces {
                $ifaces->{$1}->{exists} = 0;
                $d->{exists} = 0;
            }
-       } elsif ($iface =~ m/^(\S+)\.(\d+)$/ || $d->{'vlan-raw-device'}) {
+       } elsif ($iface =~ m/^(\S+)\.(\d+)$/) {
            $d->{type} = 'vlan';
 
            my ($dev, $id) = ($1, $2);
            $d->{'vlan-raw-device'} = $dev if defined($dev) && !$d->{'vlan-raw-device'};
+           $d->{'vlan-id'} = $id if $id; # VLAN id 0 is not valid, so truthy check it is
+
+           my $raw_iface = $d->{'vlan-raw-device'};
+
+           if (defined ($ifaces->{$raw_iface})) {
+               $d->{exists} = $ifaces->{$raw_iface}->{exists};
+           } else {
+               $ifaces->{$raw_iface}->{exists} = 0;
+               $d->{exists} = 0;
+           }
+       } elsif ($d->{'vlan-raw-device'}) {
+           $d->{type} = 'vlan';
 
-           if (!$id && $iface =~ m/^vlan(\d+)$/) { # VLAN id 0 is not valid, so truthy check it is
-               $id = $1;
+           if ($iface =~ m/^vlan(\d+)$/) {
+               $d->{'vlan-id'} = $1 if $1; # VLAN id 0 is not valid, so truthy check it is
            }
-           $d->{'vlan-id'} = $id if $id;
 
            my $raw_iface = $d->{'vlan-raw-device'};
 
@@ -1118,6 +1145,9 @@ sub __read_etc_network_interfaces {
            }
        }
 
+       log_warn("detected a interface $iface that is not a bridge!")
+           if !($d->{type} eq 'OVSBridge' || $d->{type} eq 'bridge') && $iface =~ m/^vmbr\d+$/;
+
        # map address and netmask to cidr
        if (my $addr = $d->{address}) {
            if (_address_is_cidr($addr)) {
@@ -1216,24 +1246,37 @@ sub _get_cidr {
 sub __interface_to_string {
     my ($iface, $d, $family, $first_block, $ifupdown2) = @_;
 
-    (my $suffix = $family) =~ s/^inet//;
+    my $suffix = $family;
+    $suffix =~ s/^inet// if defined($suffix);
 
-    return '' if !($d && $d->{"method$suffix"});
+    return '' if $family && !($d && $d->{"method$suffix"});
 
-    my $raw = "iface $iface $family " . $d->{"method$suffix"} . "\n";
+    my $raw = "iface $iface";
+    $raw .= " $family " . $d->{"method$suffix"} if defined $family;
+    $raw .= "\n";
 
-    if (my $addr = $d->{"address$suffix"}) {
-       if ($addr !~ /\/\d+$/ && $d->{"netmask$suffix"}) {
-           if ($d->{"netmask$suffix"} =~ m/^\d+$/) {
-               $addr .= "/" . $d->{"netmask$suffix"};
-           } elsif (my $mask = PVE::JSONSchema::get_netmask_bits($d->{"netmask$suffix"})) {
-               $addr .= "/" . $mask;
+    my $add_addr = sub {
+       my ($suffix) = @_;
+       if (my $addr = $d->{"address$suffix"}) {
+           if ($addr !~ /\/\d+$/ && $d->{"netmask$suffix"}) {
+               if ($d->{"netmask$suffix"} =~ m/^\d+$/) {
+                   $addr .= "/" . $d->{"netmask$suffix"};
+               } elsif (my $mask = PVE::JSONSchema::get_netmask_bits($d->{"netmask$suffix"})) {
+                   $addr .= "/" . $mask;
+               }
            }
+           $raw .= "\taddress ${addr}\n";
        }
-       $raw .= "\taddress ${addr}\n";
-    }
 
-    $raw .= "\tgateway " . $d->{"gateway$suffix"} . "\n" if $d->{"gateway$suffix"};
+       $raw .= "\tgateway " . $d->{"gateway$suffix"} . "\n" if $d->{"gateway$suffix"};
+    };
+
+    if ($family) {
+       $add_addr->($suffix);
+    } else {
+       $add_addr->('');
+       $add_addr->('6');
+    }
 
     my $done = {
        type => 1, priority => 1, method => 1, active => 1, exists => 1, comments => 1,
@@ -1270,7 +1313,7 @@ sub __interface_to_string {
        }
        $done->{bridge_fd} = 1;
 
-       ifdefined($d->{bridge_vlan_aware})) {
+       if (defined($d->{bridge_vlan_aware})) {
            $raw .= "\tbridge-vlan-aware yes\n";
            my $vlans = defined($d->{bridge_vids}) ? $d->{bridge_vids} : "2-4094";
            $raw .= "\tbridge-vids $vlans\n";
@@ -1280,6 +1323,7 @@ sub __interface_to_string {
 
        $raw .= "\tmtu $d->{mtu}\n" if $d->{mtu};
        $done->{mtu} = 1;
+       $done->{'bridge-disable-mac-learning'} = 1;
 
     } elsif ($d->{type} eq 'bond') {
 
@@ -1401,14 +1445,25 @@ sub __interface_to_string {
        }
     }
 
-    foreach my $option (@{$d->{"options$suffix"}}) {
-       $raw .= "\t$option\n";
-    }
+    my $add_options_comments = sub {
+       my ($suffix) = @_;
+
+       foreach my $option (@{$d->{"options$suffix"}}) {
+           $raw .= "\t$option\n";
+       }
 
-    # add comments
-    my $comments = $d->{"comments$suffix"} || '';
-    foreach my $cl (split(/\n/, $comments)) {
-       $raw .= "#$cl\n";
+       # add comments
+       my $comments = $d->{"comments$suffix"} || '';
+       foreach my $cl (split(/\n/, $comments)) {
+           $raw .= "#$cl\n";
+       }
+    };
+
+    if ($family) {
+       $add_options_comments->($suffix);
+    } else {
+       $add_options_comments->('');
+       $add_options_comments->('6');
     }
 
     $raw .= "\n";
@@ -1738,7 +1793,7 @@ NETWORKDOC
        }
 
        # if 'inet6' is the only family
-       if (scalar($d->{families}->@*) == 1 && $d->{families}[0] eq 'inet6') {
+       if (scalar($d->{families}->@*) == 1 && defined($d->{families}->[0]) && $d->{families}->[0] eq 'inet6') {
            $d->{comments6} = delete $d->{comments};
        }
 
@@ -1770,82 +1825,4 @@ sub read_iscsi_initiatorname {
 register_file('initiatorname', "/etc/iscsi/initiatorname.iscsi",
              \&read_iscsi_initiatorname);
 
-sub read_apt_auth {
-    my ($filename, $fd) = @_;
-
-    local $/;
-
-    my $raw = defined($fd) ? <$fd> : '';
-
-    $raw =~ s/^\s+//;
-
-
-    my @tokens = split(/\s+/, $raw);
-
-    my $data = {};
-
-    my $machine;
-    while (defined(my $tok = shift @tokens)) {
-
-       $machine = shift @tokens if $tok eq 'machine';
-       next if !$machine;
-       $data->{$machine} = {} if !$data->{$machine};
-
-       $data->{$machine}->{login} = shift @tokens if $tok eq 'login';
-       $data->{$machine}->{password} = shift @tokens if $tok eq 'password';
-    };
-
-    return $data;
-}
-
-my $format_apt_auth_data = sub {
-    my $data = shift;
-
-    my $raw = '';
-
-    # sort longer entries first, so machine definitions with higher granularity are preferred
-    for my $machine (sort { length($b) <=> length($a) || $a cmp $b} keys %$data) {
-       my $d = $data->{$machine};
-       next if !defined($d); # allow "deleting" set entries
-
-       $raw .= "machine $machine\n";
-       $raw .= " login $d->{login}\n" if $d->{login};
-       $raw .= " password $d->{password}\n" if $d->{password};
-       $raw .= "\n";
-    }
-
-    return $raw;
-};
-
-sub write_apt_auth {
-    my ($filename, $fh, $data) = @_;
-
-    my $raw = $format_apt_auth_data->($data);
-
-    die "write failed: $!" unless print $fh "$raw\n";
-
-    return $data;
-}
-
-sub update_apt_auth {
-    my ($filename, $fh, $data) = @_;
-
-    my $orig = read_apt_auth($filename, $fh);
-
-    foreach my $machine (keys %$data) {
-       $orig->{$machine} = $data->{$machine};
-    }
-
-    return $format_apt_auth_data->($orig);
-}
-
-register_file(
-    'apt-auth',
-    "/etc/apt/auth.conf",
-    \&read_apt_auth,
-    \&write_apt_auth,
-    \&update_apt_auth,
-    perm => 0640,
-);
-
 1;