X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FPVE%2FINotify.pm;h=871dedabebe4fd9b2609396812c4e48c3bcd3929;hb=684e07f2dd9ce50169598070e49b6c96378c7a40;hp=2aa70707cbdaaa0ff5a2d7f90856623024146399;hpb=055f076be5553a9722a9dad483e26eda8c3a7527;p=pve-common.git diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm index 2aa7070..871deda 100644 --- a/src/PVE/INotify.pm +++ b/src/PVE/INotify.pm @@ -1,28 +1,29 @@ package PVE::INotify; # todo: maybe we do not need update_file() ? - 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); @@ -923,6 +924,8 @@ sub __read_etc_network_interfaces { while (defined ($line = <$fh>)) { chomp $line; + $line =~ s/\s+$//; + if ($line =~ m/^\s*#(.*?)\s*$/) { $f->{comments} = '' if !$f->{comments}; my $comment = decode('UTF-8', $1); @@ -960,6 +963,8 @@ sub __read_etc_network_interfaces { 'bond-primary' => 1, 'uplink-id' => 1, 'vlan-protocol' => 1, + 'vlan-raw-device' => 1, + 'vlan-id' => 1, 'vxlan-id' => 1, 'vxlan-svcnodeip' => 1, 'vxlan-physdev' => 1, @@ -1031,6 +1036,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'; @@ -1050,8 +1056,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}) { @@ -1065,8 +1069,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'; @@ -1076,12 +1078,14 @@ sub __read_etc_network_interfaces { $ifaces->{$1}->{exists} = 0; $d->{exists} = 0; } - } elsif ($iface =~ m/^(\S+)\.\d+$/) { + } elsif ($iface =~ m/^(\S+)\.\d+$/ || $d->{'vlan-raw-device'}) { $d->{type} = 'vlan'; - if (defined ($ifaces->{$1})) { - $d->{exists} = $ifaces->{$1}->{exists}; + + my $raw_iface = $d->{'vlan-raw-device'} ? $d->{'vlan-raw-device'} : $1; + if (defined ($ifaces->{$raw_iface})) { + $d->{exists} = $ifaces->{$raw_iface}->{exists}; } else { - $ifaces->{$1}->{exists} = 0; + $ifaces->{$raw_iface}->{exists} = 0; $d->{exists} = 0; } } elsif ($iface =~ m/^$PVE::Network::PHYSICAL_NIC_RE$/) { @@ -1091,47 +1095,49 @@ 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->{cidr} = $d->{address} . "/" . $d->{netmask}; - } elsif ($d->{netmask} && - (my $cidr = PVE::JSONSchema::get_netmask_bits($d->{netmask}))) { # e.g. netmask 255.255.255.0 - $d->{cidr} = $d->{address} . "/" . $cidr; - } elsif ($d->{address} =~ m!^(.*)/(\d+)$!) { - $d->{cidr} = $d->{address}; - $d->{address} = $1; - $d->{netmask} = $2; + 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 { - $d->{cidr} = $d->{address}; + # no mask, else we'd got a cidr above + $d->{cidr} = $addr ."/32"; } } # map address6 and netmask6 to cidr6 - if ($d->{address6}) { - $d->{cidr6} = $d->{address6}; - if ($d->{netmask6}) { - $d->{cidr6} .= "/" . $d->{netmask6}; - } elsif ($d->{address6} =~ m!^(.*)/(\d+)$!) { - $d->{address6} = $1; - $d->{netmask6} = $2; + 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"; } } @@ -1170,6 +1176,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) = @_; @@ -1180,10 +1211,21 @@ sub __interface_to_string { my $raw = ''; $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"}; + + 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, comments => 1, autostart => 1, options => 1, @@ -1317,7 +1359,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; } @@ -1353,7 +1400,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)); } @@ -1368,8 +1415,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)) { @@ -1391,10 +1439,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}; } @@ -1494,10 +1544,31 @@ sub __write_etc_network_interfaces { # check vlan foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; - if ($d->{type} eq 'vlan' && $iface =~ m/^(\S+)\.\d+$/) { - my $p = $1; + if ($d->{type} eq 'vlan') { + + my $p = undef; + my $vlanid = undef; + + if ($iface =~ m/^(\S+)\.(\d+)$/) { + $p = $1; + $vlanid = $2; + delete $d->{'vlan-raw-device'} if $d->{'vlan-raw-device'}; + } else { + die "missing vlan-raw-device option" if !$d->{'vlan-raw-device'}; + $p = $d->{'vlan-raw-device'}; + + if ($iface =~ m/^vlan(\d+)$/) { + $vlanid = $1; + delete $d->{'vlan-id'} if $d->{'vlan-id'}; + } else { + die "custom vlan interface name need ifupdown2" if !$ifupdown2; + die "missing vlan-id option" if !$d->{'vlan-id'}; + $vlanid = $d->{'vlan-id'}; + } + } my $n = $ifaces->{$p}; + die "vlan '$iface' - vlan-id $vlanid should be <= 4094\n" if $vlanid > 4094; die "vlan '$iface' - unable to find parent '$p'\n" if !$n; @@ -1529,18 +1600,26 @@ sub __write_etc_network_interfaces { # check bridgeport option my $bridgeports = {}; my $bridges = {}; - foreach my $iface (keys %$ifaces) { - my $d = $ifaces->{$iface}; + my $ifaces_copy = { %$ifaces }; + foreach my $iface (keys %$ifaces_copy) { + my $d = $ifaces_copy->{$iface}; if ($d->{type} eq 'bridge') { foreach my $p (split (/\s+/, $d->{bridge_ports})) { - $p =~ s/\.\d+$//; - my $n = $ifaces->{$p}; + if($p =~ m/(\S+)\.(\d+)$/) { + my $vlanparent = $1; + if (!defined($ifaces_copy->{$p})) { + $ifaces_copy->{$p}->{type} = 'vlan'; + $ifaces_copy->{$p}->{method} = 'manual'; + $ifaces_copy->{$p}->{method6} = 'manual'; + $ifaces_copy->{$p}->{mtu} = $ifaces_copy->{$vlanparent}->{mtu} if defined($ifaces_copy->{$1}->{mtu}); + } + } + my $n = $ifaces_copy->{$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); + if ($n->{method} && $n->{method} eq 'static' && $n->{address} ne '0.0.0.0') || + ($n->{method6} && $n->{method6} eq 'static' && $n->{address} ne '::'); + &$check_mtu($ifaces_copy, $p, $iface); $bridgeports->{$p} = $iface; } $bridges->{$iface} = $d; @@ -1580,11 +1659,13 @@ NETWORKDOC loopback => 100000, eth => 200000, OVSPort => 200000, - OVSIntPort => 200000, - bond => 300000, - bridge => 400000, - OVSBridge => 400000, - vxlan => 500000, + OVSIntPort => 300000, + OVSBond => 400000, + bond => 400000, + bridge => 500000, + OVSBridge => 500000, + vlan => 600000, + vxlan => 600000, }; my $lookup_type_prio = sub { @@ -1629,7 +1710,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 {