X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FINotify.pm;h=2bb87bb2dd71e79057dcaa2da391316686e7bc23;hp=a4de45a7355ccb15bc04dc2554fbb28c63047caf;hb=9ca89b8722fb9656b5deaca037f0612aab3170e4;hpb=beedabb4aba8083f2ebe9f29c51b19bf1d4149fd diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm index a4de45a..2bb87bb 100644 --- a/src/PVE/INotify.pm +++ b/src/PVE/INotify.pm @@ -4,24 +4,26 @@ package PVE::INotify; use strict; use warnings; -use POSIX; -use IO::File; -use IO::Dir; -use File::stat; -use File::Basename; +use Clone qw(clone); +use Digest::SHA; +use Encode qw(encode decode); use Fcntl qw(:DEFAULT :flock); -use PVE::SafeSyslog; +use File::Basename; +use File::stat; +use IO::Dir; +use IO::File; +use JSON; +use Linux::Inotify2; +use POSIX; + use PVE::Exception qw(raise_param_exc); +use PVE::JSONSchema; use PVE::Network; -use PVE::Tools; use PVE::ProcFSTools; -use PVE::JSONSchema; -use Clone qw(clone); -use Linux::Inotify2; +use PVE::SafeSyslog; +use PVE::Tools; + use base 'Exporter'; -use JSON; -use Digest::SHA; -use Encode qw(encode decode); our @EXPORT_OK = qw(read_file write_file register_file); @@ -921,7 +923,8 @@ sub __read_etc_network_interfaces { push @{$d->{families}}, $family; while (defined ($line = <$fh>)) { - chomp $line; + $line =~ s/\s+$//; # drop trailing whitespaces + if ($line =~ m/^\s*#(.*?)\s*$/) { $f->{comments} = '' if !$f->{comments}; my $comment = decode('UTF-8', $1); @@ -1032,6 +1035,7 @@ sub __read_etc_network_interfaces { foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; + $d->{type} = 'unknown'; if ($iface =~ m/^bond\d+$/) { if (!$d->{ovs_type}) { $d->{type} = 'bond'; @@ -1051,8 +1055,6 @@ sub __read_etc_network_interfaces { } my $tag = &$extract_ovs_option($d, 'tag'); $d->{ovs_tag} = $tag if defined($tag); - } else { - $d->{type} = 'unknown'; } } elsif ($iface =~ m/^vmbr\d+$/) { if (!$d->{ovs_type}) { @@ -1066,8 +1068,6 @@ sub __read_etc_network_interfaces { } } elsif ($d->{ovs_type} eq 'OVSBridge') { $d->{type} = $d->{ovs_type}; - } else { - $d->{type} = 'unknown'; } } elsif ($iface =~ m/^(\S+):\d+$/) { $d->{type} = 'alias'; @@ -1094,40 +1094,50 @@ sub __read_etc_network_interfaces { $d->{type} = $d->{ovs_type}; my $tag = &$extract_ovs_option($d, 'tag'); $d->{ovs_tag} = $tag if defined($tag); - } else { - $d->{type} = 'unknown'; } } elsif ($iface =~ m/^lo$/) { $d->{type} = 'loopback'; } else { if ($d->{'vxlan-id'}) { $d->{type} = 'vxlan'; - } elsif (!$d->{ovs_type}) { - $d->{type} = 'unknown'; - } elsif ($d->{ovs_type} eq 'OVSIntPort') { - $d->{type} = $d->{ovs_type}; - my $tag = &$extract_ovs_option($d, 'tag'); - $d->{ovs_tag} = $tag if defined($tag); + } elsif (defined($d->{ovs_type})) { + if ($d->{ovs_type} eq 'OVSIntPort') { + $d->{type} = $d->{ovs_type}; + my $tag = &$extract_ovs_option($d, 'tag'); + $d->{ovs_tag} = $tag if defined($tag); + } } } # map address and netmask to cidr - if ($d->{address}) { - if ($d->{netmask} && $d->{netmask} =~ m/^\d+$/) { # e.g. netmask 20 - $d->{address} = $d->{address} . "/" . $d->{netmask}; - } elsif ($d->{netmask} && - (my $cidr = PVE::JSONSchema::get_netmask_bits($d->{netmask}))) { # e.g. netmask 255.255.255.0 - $d->{address} = $d->{address} . "/" . $cidr; + if (my $addr = $d->{address}) { + if (_address_is_cidr($addr)) { + $d->{cidr} = $addr; + my ($baseaddr, $mask) = _cidr_split($addr); + $d->{address} = $baseaddr; + $d->{netmask} = $mask; + } elsif (my $cidr = _get_cidr($d->{address}, $d->{netmask})) { + $d->{cidr} = $cidr; + (undef, $d->{netmask}) = _cidr_split($cidr); + } else { + # no mask, else we'd got a cidr above + $d->{cidr} = $addr ."/32"; } - #for api compatibility - $d->{cidr} = $d->{address} } # map address6 and netmask6 to cidr6 - if ($d->{address6}) { - $d->{address6} .= "/" . $d->{netmask6} if $d->{address6} !~ m!^(.*)/(\d+)$! && $d->{netmask6}; - #for api compatibility - $d->{cidr6} = $d->{address6} + if (my $addr6 = $d->{address6}) { + if (_address_is_cidr($addr6)) { + $d->{cidr6} = $addr6; + my ($baseaddr, $mask) = _cidr_split($addr6); + $d->{address6} = $baseaddr; + $d->{netmask6} = $mask; + } elsif (my $cidr6 = _get_cidr($d->{address6}, $d->{netmask6})) { + $d->{cidr6} = $cidr6; + } else { + # no mask, else we'd got a cidr above + $d->{cidr6} = $addr6 ."/128"; + } } $d->{method} = 'manual' if !$d->{method}; @@ -1165,6 +1175,31 @@ sub __read_etc_network_interfaces { return $config; } +sub _address_is_cidr { + my ($addr) = @_; + return $addr =~ /\/\d+$/ ? 1 : 0; +} + +sub _cidr_split { + my ($cidr) = @_; + $cidr =~ /^(.+)\/(\d+)$/; + return ($1, $2); # (address, mask) +} + +sub _get_cidr { + my ($addr, $mask) = @_; + + return $addr if _address_is_cidr($addr); + return undef if !$mask; + + if ($mask =~ m/^\d+$/) { # cidr notation + return $addr . "/" . $mask; + } elsif (my $cidrmask = PVE::JSONSchema::get_netmask_bits($mask)) { + return $addr . "/" . $cidrmask; + } + return undef; +} + sub __interface_to_string { my ($iface, $d, $family, $first_block, $ifupdown2) = @_; @@ -1175,7 +1210,20 @@ sub __interface_to_string { my $raw = ''; $raw .= "iface $iface $family " . $d->{"method$suffix"} . "\n"; - $raw .= "\taddress " . $d->{"address$suffix"} . "\n" if $d->{"address$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 .= "\tgateway " . $d->{"gateway$suffix"} . "\n" if $d->{"gateway$suffix"}; my $done = { type => 1, priority => 1, method => 1, active => 1, exists => 1, @@ -1188,8 +1236,8 @@ sub __interface_to_string { # not printing out options } elsif ($d->{type} eq 'bridge') { - $d->{bridge_ports} =~ s/[;,\s]+/ /g; my $ports = $d->{bridge_ports} || 'none'; + $ports =~ s/[;,\s]+/ /g; $raw .= "\tbridge-ports $ports\n"; $done->{bridge_ports} = 1; @@ -1310,7 +1358,12 @@ sub __interface_to_string { $done->{ovs_type} = 1; if (my $bridge = $d->{ovs_bridge}) { - $raw = "allow-$bridge $iface\n$raw"; + if ($ifupdown2) { + $raw = "auto $iface\n$raw"; + } else { + $raw = "allow-$bridge $iface\n$raw"; + } + $raw .= "\tovs_bridge $bridge\n"; $done->{ovs_bridge} = 1; } @@ -1346,7 +1399,7 @@ sub __interface_to_string { sub write_etc_network_interfaces { my ($filename, $fh, $config) = @_; - my $ifupdown2 = -e '/usr/share/ifupdown2'; + my $ifupdown2 = -e '/usr/share/ifupdown2/ifupdown2'; my $raw = __write_etc_network_interfaces($config, $ifupdown2); PVE::Tools::safe_print($filename, $fh, encode('UTF-8', $raw)); } @@ -1361,8 +1414,9 @@ sub __write_etc_network_interfaces { foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; - delete $d->{cidr}; - delete $d->{cidr6}; + my ($cidr, $cidr6) = (delete $d->{cidr}, delete $d->{cidr6}); + $d->{address} //= $cidr; + $d->{address6} //= $cidr6; my $ports = ''; foreach my $k (qw(bridge_ports ovs_ports slaves ovs_bonds)) { @@ -1384,10 +1438,12 @@ sub __write_etc_network_interfaces { my $brname = $used_ports->{$iface}; if (!$brname || !$ifaces->{$brname}) { if ($iface =~ /^$PVE::Network::PHYSICAL_NIC_RE/) { - $ifaces->{$iface} = { type => 'eth', - exists => 1, - method => 'manual', - families => ['inet'] }; + $ifaces->{$iface} = { + type => 'eth', + exists => 1, + method => 'manual', + families => ['inet'], + }; } else { delete $ifaces->{$iface}; } @@ -1431,6 +1487,7 @@ sub __write_etc_network_interfaces { if ($d->{type} eq 'OVSBond' && $d->{ovs_bonds}) { foreach my $p (split (/\s+/, $d->{ovs_bonds})) { my $n = $ifaces->{$p}; + $n->{autostart} = 1; die "OVS bond '$iface' - unable to find slave '$p'\n" if !$n; die "OVS bond '$iface' - wrong interface type on slave '$p' " . @@ -1443,20 +1500,21 @@ sub __write_etc_network_interfaces { # check bond foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; - if ($d->{type} eq 'bond' && $d->{slaves}) { - my $bond_primary_is_slave = undef; - foreach my $p (split (/\s+/, $d->{slaves})) { - my $n = $ifaces->{$p}; + next if !($d->{type} eq 'bond' && $d->{slaves}); - die "bond '$iface' - unable to find slave '$p'\n" - if !$n; - die "bond '$iface' - wrong interface type on slave '$p' " . - "('$n->{type}' != 'eth')\n" if $n->{type} ne 'eth'; - &$check_mtu($ifaces, $iface, $p); - $bond_primary_is_slave = 1 if $d->{'bond-primary'} && $d->{'bond-primary'} eq $p; - } - die "bond '$iface' - bond-primary interface is not a slave" if $d->{'bond-primary'} && !$bond_primary_is_slave; + my $bond_primary_is_slave = undef; + foreach my $p (split (/\s+/, $d->{slaves})) { + my $n = $ifaces->{$p}; + $n->{autostart} = 1; + + die "bond '$iface' - unable to find slave '$p'\n" if !$n; + die "bond '$iface' - wrong interface type on slave '$p' ('$n->{type}' != 'eth or bond')\n" + if ($n->{type} ne 'eth' && $n->{type} ne 'bond'); + + $check_mtu->($ifaces, $iface, $p); + $bond_primary_is_slave = 1 if $d->{'bond-primary'} && $d->{'bond-primary'} eq $p; } + die "bond '$iface' - bond-primary interface is not a slave" if $d->{'bond-primary'} && !$bond_primary_is_slave; } # check vxlan @@ -1496,6 +1554,8 @@ sub __write_etc_network_interfaces { $p = $1; $vlanid = $2; delete $d->{'vlan-raw-device'} if $d->{'vlan-raw-device'}; + delete $d->{'vlan-id'} if $d->{'vlan-id'}; + } else { die "missing vlan-raw-device option" if !$d->{'vlan-raw-device'}; $p = $d->{'vlan-raw-device'}; @@ -1547,7 +1607,7 @@ sub __write_etc_network_interfaces { foreach my $iface (keys %$ifaces_copy) { my $d = $ifaces_copy->{$iface}; if ($d->{type} eq 'bridge') { - foreach my $p (split (/\s+/, $d->{bridge_ports})) { + foreach my $p (split (/\s+/, $d->{bridge_ports} // '')) { if($p =~ m/(\S+)\.(\d+)$/) { my $vlanparent = $1; if (!defined($ifaces_copy->{$p})) { @@ -1561,7 +1621,7 @@ sub __write_etc_network_interfaces { 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} && $n->{method} eq 'static' && $n->{address} ne '0.0.0.0') || - ($n->{method6} && $n->{method6} eq 'static' && $n->{address} ne '::'); + ($n->{method6} && $n->{method6} eq 'static' && $n->{address6} ne '::'); &$check_mtu($ifaces_copy, $p, $iface); $bridgeports->{$p} = $iface; } @@ -1602,12 +1662,13 @@ NETWORKDOC loopback => 100000, eth => 200000, OVSPort => 200000, - OVSIntPort => 200000, - bond => 300000, - bridge => 400000, - OVSBridge => 400000, - vlan => 500000, - vxlan => 500000, + OVSIntPort => 300000, + OVSBond => 400000, + bond => 400000, + bridge => 500000, + OVSBridge => 500000, + vlan => 600000, + vxlan => 600000, }; my $lookup_type_prio = sub { @@ -1615,12 +1676,10 @@ NETWORKDOC my ($rootiface, @rest) = split(/[.:]/, $iface); my $childlevel = scalar(@rest); - my $n = $ifaces->{$rootiface}; + my $type = $ifaces->{$rootiface}->{type}; + return if !$type || $type eq 'unknown'; - my $pri = $if_type_hash->{$n->{type}} + $childlevel - if $n->{type} && $n->{type} ne 'unknown'; - - return $pri; + return $if_type_hash->{$type} + $childlevel }; foreach my $iface (sort { @@ -1652,7 +1711,7 @@ NETWORKDOC $printed->{$iface} = 1; if ($d->{autostart}) { - if ($d->{type} eq 'OVSBridge') { + if ($d->{type} eq 'OVSBridge' && !$ifupdown2) { # cannot use 'auto' for OVS, would add race with systemd ifup@.service $raw .= "allow-ovs $iface\n"; } else { @@ -1721,8 +1780,11 @@ my $format_apt_auth_data = sub { my $raw = ''; - foreach my $machine (sort keys %$data) { + # 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}; @@ -1735,7 +1797,7 @@ my $format_apt_auth_data = sub { sub write_apt_auth { my ($filename, $fh, $data) = @_; - my $raw = &$format_apt_auth_data($data); + my $raw = $format_apt_auth_data->($data); die "write failed: $!" unless print $fh "$raw\n"; @@ -1751,11 +1813,16 @@ sub update_apt_auth { $orig->{$machine} = $data->{$machine}; } - return &$format_apt_auth_data($orig); + 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); +register_file( + 'apt-auth', + "/etc/apt/auth.conf", + \&read_apt_auth, + \&write_apt_auth, + \&update_apt_auth, + perm => 0640, +); 1;