X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FINotify.pm;h=11b672850c19d5cbe3ab3a4c359612472aff4a07;hp=f0f3144355c7d35319b9e4e2cc39f82bbbc7b29b;hb=2896245e759b72c136785f22d6d0edaa23248b32;hpb=c4e564708b38c9863bb946778580704e48f3b8b2 diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm index f0f3144..11b6728 100644 --- a/src/PVE/INotify.pm +++ b/src/PVE/INotify.pm @@ -16,10 +16,13 @@ use PVE::Exception qw(raise_param_exc); use PVE::Network; use PVE::Tools; use PVE::ProcFSTools; +use PVE::JSONSchema; use Clone qw(clone); use Linux::Inotify2; use base 'Exporter'; -use JSON; +use JSON; +use Digest::SHA; +use Encode qw(encode decode); our @EXPORT_OK = qw(read_file write_file register_file); @@ -56,13 +59,11 @@ sub ccache_compute_diff { my $diff = ''; - open (TMP, "diff -b -N -u '$filename' '$shadow'|"); - - while (my $line = ) { - $diff .= $line; - } - - close (TMP); + my $cmd = ['/usr/bin/diff', '-b', '-N', '-u', $filename, $shadow]; + PVE::Tools::run_command($cmd, noerr => 1, outfunc => sub { + my ($line) = @_; + $diff .= decode('UTF-8', $line) . "\n"; + }); $diff = undef if !$diff; @@ -538,6 +539,54 @@ register_file('hostname', "/etc/hostname", \&read_etc_hostname, \&write_etc_hostname); +sub read_etc_hosts { + my ($filename, $fh) = @_; + + my $raw = ''; + my $data = ''; + + while (my $line = <$fh>) { + $raw .= $line; + if ($line =~ m/^\s*#/) { + $line = decode('UTF-8', $line); + } + $data .= $line; + } + + return { + digest => Digest::SHA::sha1_hex($raw), + data => $data, + } +} + +sub write_etc_hosts { + my ($filename, $fh, $hosts, @args) = @_; + + # check validity of ips/names + for my $line (split("\n", $hosts)) { + next if $line =~ m/^\s*#/; # comments + next if $line =~ m/^\s*$/; # whitespace/empty lines + + my ($ip, @names) = split(/\s+/, $line); + + raise_param_exc({ 'data' => "Invalid IP '$ip'" }) + if $ip !~ m/^$PVE::Tools::IPRE$/; + + for my $name (@names) { + raise_param_exc({ 'data' => "Invalid Hostname '$name'" }) + if $name !~ m/^[.\-a-zA-Z0-9]+$/; + } + } + + die "write failed: $!" if !print $fh encode('UTF-8', $hosts); + + return $hosts; +} + +register_file('etchosts', "/etc/hosts", + \&read_etc_hosts, + \&write_etc_hosts); + sub read_etc_resolv_conf { my ($filename, $fh) = @_; @@ -753,6 +802,24 @@ my $extract_ovs_option = sub { return $v; }; +my $check_mtu = sub { + my ($ifaces, $parent, $child) = @_; + + die "check mtu - missing parent interface\n" if !$parent; + die "check mtu - missing child interface\n" if !$child; + + my $cmtu = $ifaces->{$child}->{mtu}; + return if !$cmtu; + + my $parentdata = $ifaces->{$parent}; + my $pmtu = $parentdata->{mtu}; + $pmtu = $cmtu if $parentdata->{type} eq 'bond' && !$pmtu; + $pmtu = 1500 if !$pmtu; + + die "interface '$parent' - mtu $pmtu is lower than '$child' - mtu $cmtu\n" + if $pmtu < $cmtu; +}; + # config => { # ifaces => { # $ifname => { @@ -855,10 +922,9 @@ sub __read_etc_network_interfaces { while (defined ($line = <$fh>)) { chomp $line; if ($line =~ m/^\s*#(.*?)\s*$/) { - # NOTE: we use 'comments' instead of 'comment' to - # avoid automatic utf8 conversion $f->{comments} = '' if !$f->{comments}; - $f->{comments} .= "$1\n"; + my $comment = decode('UTF-8', $1); + $f->{comments} .= "$comment\n"; } elsif ($line =~ m/^\s*(?:iface\s |mapping\s |auto\s @@ -874,6 +940,7 @@ sub __read_etc_network_interfaces { $id = $options_alternatives->{$id} if $options_alternatives->{$id}; my $simple_options = { + 'mtu' => 1, 'ovs_type' => 1, 'ovs_options' => 1, 'ovs_bridge' => 1, @@ -888,6 +955,7 @@ sub __read_etc_network_interfaces { 'bridge-multicast-flood' => 1, 'bond_miimon' => 1, 'bond_xmit_hash_policy' => 1, + 'vlan-protocol' => 1, 'vxlan-id' => 1, 'vxlan-svcnodeip' => 1, 'vxlan-physdev' => 1, @@ -1036,6 +1104,34 @@ sub __read_etc_network_interfaces { } } + # map address and netmask to cidr + if ($d->{address}) { + if ($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; + } else { + $d->{cidr} = $d->{address}; + } + } + + # map address6 and netmask6 to cidr6 + if ($d->{address6}) { + $d->{cidr6} = $d->{address6}; + if ($d->{netmask6}) { + $d->{cidr6} .= "/" . $d->{netmask6} if $d->{netmask6}; + } elsif ($d->{address6} =~ m!^(.*)/(\d+)$!) { + $d->{cidr6} = $d->{address6}; + $d->{address6} = $1; + $d->{netmask6} = $2; + } + } + $d->{method} = 'manual' if !$d->{method}; $d->{method6} = 'manual' if !$d->{method6}; @@ -1135,12 +1231,16 @@ sub __interface_to_string { $raw .= "\tbond-xmit-hash-policy $d->{'bond_xmit_hash_policy'}\n"; } $done->{'bond_xmit_hash_policy'} = 1; + } elsif ($d->{type} eq 'vlan') { + 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)) { - $raw .= "\t$k $d->{$k}\n" if $d->{$k}; + foreach my $k (qw(vxlan-id vxlan-svcnodeip vxlan-physdev vxlan-local-tunnelip)) { + $raw .= "\t$k $d->{$k}\n" if defined $d->{$k}; $done->{$k} = 1; - } + } if ($d->{'vxlan-remoteip'}) { foreach my $remoteip (@{$d->{'vxlan-remoteip'}}) { @@ -1233,7 +1333,7 @@ sub write_etc_network_interfaces { my ($filename, $fh, $config) = @_; my $ifupdown2 = -e '/usr/share/ifupdown2'; my $raw = __write_etc_network_interfaces($config, $ifupdown2); - PVE::Tools::safe_print($filename, $fh, $raw); + PVE::Tools::safe_print($filename, $fh, encode('UTF-8', $raw)); } sub __write_etc_network_interfaces { my ($config, $ifupdown2) = @_; @@ -1246,6 +1346,9 @@ sub __write_etc_network_interfaces { foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; + delete $d->{cidr}; + delete $d->{cidr6}; + my $ports = ''; foreach my $k (qw(bridge_ports ovs_ports slaves ovs_bonds)) { $ports .= " $d->{$k}" if $d->{$k}; @@ -1261,10 +1364,10 @@ sub __write_etc_network_interfaces { # delete unused OVS ports foreach my $iface (keys %$ifaces) { my $d = $ifaces->{$iface}; - if ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' || + if ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' || $d->{type} eq 'OVSBond') { my $brname = $used_ports->{$iface}; - if (!$brname || !$ifaces->{$brname}) { + if (!$brname || !$ifaces->{$brname}) { if ($iface =~ /^$PVE::Network::PHYSICAL_NIC_RE/) { $ifaces->{$iface} = { type => 'eth', exists => 1, @@ -1294,13 +1397,15 @@ sub __write_etc_network_interfaces { $n->{autostart} = 0; if ($n->{type} eq 'eth') { $n->{type} = 'OVSPort'; - $n->{ovs_bridge} = $iface; + $n->{ovs_bridge} = $iface; } elsif ($n->{type} eq 'OVSBond' || $n->{type} eq 'OVSPort' || $n->{type} eq 'OVSIntPort') { $n->{ovs_bridge} = $iface; } else { die "interface '$p' is not defined as OVS port/bond\n"; } + + &$check_mtu($ifaces, $iface, $p); } } } @@ -1315,6 +1420,7 @@ sub __write_etc_network_interfaces { if !$n; die "OVS bond '$iface' - wrong interface type on slave '$p' " . "('$n->{type}' != 'eth')\n" if $n->{type} ne 'eth'; + &$check_mtu($ifaces, $iface, $p); } } } @@ -1330,6 +1436,7 @@ sub __write_etc_network_interfaces { 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); } } } @@ -1341,7 +1448,7 @@ sub __write_etc_network_interfaces { if ($d->{type} eq 'vxlan' && $d->{'vxlan-id'}) { my $vxlanid = $d->{'vxlan-id'}; - die "iface $iface : duplicate vxlan-id $vxlanid already used in $vxlans->{$vxlanid}\n" if $vxlans->{$vxlanid}; + die "iface $iface - duplicate vxlan-id $vxlanid already used in $vxlans->{$vxlanid}\n" if $vxlans->{$vxlanid}; $vxlans->{$vxlanid} = $iface; } @@ -1350,12 +1457,13 @@ sub __write_etc_network_interfaces { ++$ips if defined $d->{'vxlan-remoteip'}; ++$ips if defined $d->{'vxlan-local-tunnelip'}; if ($ips > 1) { - die "ifac $iface : vxlan-svcnodeip, vxlan-remoteip and vxlan-localtunnelip are mutually exclusive\n"; + die "iface $iface - vxlan-svcnodeip, vxlan-remoteip and vxlan-localtunnelip are mutually exclusive\n"; } if (defined($d->{'vxlan-svcnodeip'}) != defined($d->{'vxlan-physdev'})) { - die "iface $iface : vxlan-svcnodeip and vxlan-physdev must be define together\n"; + die "iface $iface - vxlan-svcnodeip and vxlan-physdev must be define together\n"; } + #fixme : check if vxlan mtu is lower than 50bytes than physical interface where tunnel is going out } # check vlan @@ -1366,14 +1474,17 @@ sub __write_etc_network_interfaces { my $n = $ifaces->{$p}; die "vlan '$iface' - unable to find parent '$p'\n" - if $n->{exists} eq 0; + 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') { + } elsif ($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' )\n"; + "('$n->{type}' != 'eth|bond|bridge|vlan' )\n"; } + + &$check_mtu($ifaces, $p, $iface); + } } @@ -1384,9 +1495,11 @@ sub __write_etc_network_interfaces { my $d = $ifaces->{$iface}; if ($d->{type} eq 'bridge') { foreach my $p (split (/\s+/, $d->{bridge_ports})) { + $p =~ s/\.\d+$//; my $n = $ifaces->{$p}; die "bridge '$iface' - unable to find bridge port '$p'\n" if !$n; + &$check_mtu($ifaces, $iface, $p); $bridgeports->{$p} = $iface; } $bridges->{$iface} = $d; @@ -1397,12 +1510,12 @@ sub __write_etc_network_interfaces { my $d = $ifaces->{$iface}; foreach my $k (qw(bridge-learning bridge-arp-nd-suppress bridge-unicast-flood bridge-multicast-flood bridge-access)) { - die "iface $iface : bridgeports options can be used only if interface is in a bridge\n" + die "iface $iface - $k: bridge port specific options can be used only on interfaces attached to a bridge\n" if $d->{$k} && !$bridgeports->{$iface}; } if ($d->{'bridge-access'} && !$bridges->{$bridgeports->{$iface}}->{bridge_vlan_aware}) { - die "iface $iface : bridge-access option can be only used if interface is in a vlan aware bridge\n"; + die "iface $iface - bridge-access option can be only used if interface is in a vlan aware bridge\n"; } } @@ -1411,10 +1524,10 @@ sub __write_etc_network_interfaces { # Please do NOT modify this file directly, unless you know what # you're doing. # -# If you want to manage part of the network configuration manually, +# If you want to manage parts of the network configuration manually, # please utilize the 'source' or 'source-directory' directives to do # so. -# PVE will preserve these directives, but will NOT its network +# PVE will preserve these directives, but will NOT read its network # configuration from sourced files, so do not attempt to move any of # the PVE managed interfaces into external files! @@ -1425,30 +1538,23 @@ NETWORKDOC my $if_type_hash = { loopback => 100000, eth => 200000, + OVSPort => 200000, + OVSIntPort => 200000, bond => 300000, bridge => 400000, + OVSBridge => 400000, vxlan => 500000, }; my $lookup_type_prio = sub { - my $iface = shift; + my ($iface, $ifaces) = @_; - my $child = 0; - if ($iface =~ m/^(\S+)(\.|:)\d+$/) { - $iface = $1; - $child = 1; - } + my ($rootiface, @rest) = split(/[.:]/, $iface); + my $childlevel = scalar(@rest); + my $n = $ifaces->{$rootiface}; - my $pri; - if ($iface eq 'lo') { - $pri = $if_type_hash->{loopback}; - } elsif ($iface =~ m/^$PVE::Network::PHYSICAL_NIC_RE$/) { - $pri = $if_type_hash->{eth} + $child; - } elsif ($iface =~ m/^bond\d+$/) { - $pri = $if_type_hash->{bond} + $child; - } elsif ($iface =~ m/^vmbr\d+$/) { - $pri = $if_type_hash->{bridge} + $child; - } + my $pri = $if_type_hash->{$n->{type}} + $childlevel + if $n->{type} && $n->{type} ne 'unknown'; return $pri; }; @@ -1456,8 +1562,8 @@ NETWORKDOC foreach my $iface (sort { my $ref1 = $ifaces->{$a}; my $ref2 = $ifaces->{$b}; - my $tp1 = &$lookup_type_prio($a); - my $tp2 = &$lookup_type_prio($b); + my $tp1 = &$lookup_type_prio($a, $ifaces); + my $tp2 = &$lookup_type_prio($b, $ifaces); # Only recognized types are in relation to each other. If one type # is unknown then only consider the interfaces' priority attributes.