X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FPVE%2FINotify.pm;h=a4de45a7355ccb15bc04dc2554fbb28c63047caf;hb=beedabb4aba8083f2ebe9f29c51b19bf1d4149fd;hp=8b49f5a1a2061f89d6cc31564ae7ff25a6332925;hpb=4515aaec1ea703a4de7648ebb8db363664577509;p=pve-common.git diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm index 8b49f5a..a4de45a 100644 --- a/src/PVE/INotify.pm +++ b/src/PVE/INotify.pm @@ -1,7 +1,6 @@ package PVE::INotify; # todo: maybe we do not need update_file() ? - use strict; use warnings; @@ -84,15 +83,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 +134,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 +167,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 +215,9 @@ sub read_file { my $parser; my ($ccinfo, $filename) = ccache_info($fileid); - + $parser = $ccinfo->{parser}; - + my $fd; my $shadow; @@ -238,7 +237,7 @@ sub read_file { if (!$fd) { $ccinfo->{version} = undef; - $ccinfo->{data} = undef; + $ccinfo->{data} = undef; $ccinfo->{diff} = undef; return undef if !$acp; } @@ -260,7 +259,7 @@ sub read_file { $ret->{data} = $ccinfo->{data}; } $ret->{changes} = $ccinfo->{diff}; - + return $full ? $ret : $ret->{data}; } @@ -293,7 +292,7 @@ sub read_file { $ret->{changes} = $ccinfo->{diff}; return $full ? $ret : $ret->{data}; -} +} sub parse_ccache_options { my ($ccinfo, %options) = @_; @@ -312,7 +311,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 +336,7 @@ sub register_file { $ccinfo->{update} = $update; parse_ccache_options($ccinfo, %options); - + if ($options{shadow}) { $shadowfiles->{$filename} = $options{shadow}; } @@ -353,7 +352,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 +416,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 +446,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 +479,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 +534,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 +628,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 +663,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 +672,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 +716,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 +734,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 { @@ -872,6 +871,7 @@ sub __read_etc_network_interfaces { my $options = $config->{options} = []; my $options_alternatives = { + 'ovs_mtu' => 'mtu', 'bond-slaves' => 'slaves', 'bond_slaves' => 'slaves', 'bond-xmit-hash-policy' => 'bond_xmit_hash_policy', @@ -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,7 +956,11 @@ sub __read_etc_network_interfaces { 'bridge-multicast-flood' => 1, 'bond_miimon' => 1, 'bond_xmit_hash_policy' => 1, + '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, @@ -1072,12 +1077,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$/) { @@ -1107,28 +1114,20 @@ sub __read_etc_network_interfaces { # 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}; + $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->{cidr} = $d->{address} . "/" . $cidr; - } elsif ($d->{address} =~ m!^(.*)/(\d+)$!) { - $d->{cidr} = $d->{address}; - $d->{address} = $1; - $d->{netmask} = $2; - } else { - $d->{cidr} = $d->{address}; + $d->{address} = $d->{address} . "/" . $cidr; } + #for api compatibility + $d->{cidr} = $d->{address} } # 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; - } + $d->{address6} .= "/" . $d->{netmask6} if $d->{address6} !~ m!^(.*)/(\d+)$! && $d->{netmask6}; + #for api compatibility + $d->{cidr6} = $d->{address6} } $d->{method} = 'manual' if !$d->{method}; @@ -1141,7 +1140,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})) { @@ -1174,16 +1175,14 @@ 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"}; + $raw .= "\taddress " . $d->{"address$suffix"} . "\n" if $d->{"address$suffix"}; + $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, 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 +1208,10 @@ sub __interface_to_string { } $done->{bridge_vlan_aware} = 1; $done->{bridge_vids} = 1; - + + $raw .= "\tmtu $d->{mtu}\n" if $d->{mtu}; + $done->{mtu} = 1; + } elsif ($d->{type} eq 'bond') { $d->{slaves} =~ s/[;,\s]+/ /g; @@ -1230,10 +1232,19 @@ sub __interface_to_string { $raw .= "\tbond-xmit-hash-policy $d->{'bond_xmit_hash_policy'}\n"; } $done->{'bond_xmit_hash_policy'} = 1; + + if ($d->{'bond_mode'} && $d->{'bond_mode'} eq 'active-backup' && $d->{'bond-primary'}) { + $raw .= "\tbond-primary $d->{'bond-primary'}\n"; + } + $done->{'bond-primary'} = 1; + + $raw .= "\tmtu $d->{mtu}\n" if $d->{mtu}; + $done->{mtu} = 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)) { @@ -1247,6 +1258,10 @@ sub __interface_to_string { } $done->{'vxlan-remoteip'} = 1; } + + $raw .= "\tmtu $d->{mtu}\n" if $d->{mtu}; + $done->{mtu} = 1; + } elsif ($d->{type} eq 'OVSBridge') { $raw .= "\tovs_type $d->{type}\n"; @@ -1254,6 +1269,10 @@ sub __interface_to_string { $raw .= "\tovs_ports $d->{ovs_ports}\n" if $d->{ovs_ports}; $done->{ovs_ports} = 1; + + $raw .= "\tovs_mtu $d->{mtu}\n" if $d->{mtu}; + $done->{mtu} = 1; + } elsif ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' || $d->{type} eq 'OVSBond') { @@ -1290,17 +1309,14 @@ 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; } + + $raw .= "\tovs_mtu $d->{mtu}\n" if $d->{mtu}; + $done->{mtu} = 1; } if ($first_block) { @@ -1428,6 +1444,7 @@ sub __write_etc_network_interfaces { 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}; @@ -1436,7 +1453,9 @@ sub __write_etc_network_interfaces { 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; } } @@ -1468,16 +1487,35 @@ 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; - 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,18 +1525,44 @@ 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 = {}; - 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}; - die "bridge '$iface' - unable to find bridge port '$p'\n" - if !$n; - &$check_mtu($ifaces, $iface, $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} && $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; @@ -1542,6 +1606,7 @@ NETWORKDOC bond => 300000, bridge => 400000, OVSBridge => 400000, + vlan => 500000, vxlan => 500000, }; @@ -1576,7 +1641,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 +1651,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 +1685,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 +1697,7 @@ sub read_apt_auth { $raw =~ s/^\s+//; - + my @tokens = split(/\s+/, $raw); my $data = {}; @@ -1666,7 +1738,7 @@ sub write_apt_auth { my $raw = &$format_apt_auth_data($data); die "write failed: $!" unless print $fh "$raw\n"; - + return $data; } @@ -1682,7 +1754,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);