mirror of
git://git.proxmox.com/git/pve-common.git
synced 2024-12-22 21:33:47 +03:00
importing etc_network_interfaces tests
This commit is contained in:
parent
516a50a4ac
commit
936218b86f
3
Makefile
3
Makefile
@ -40,6 +40,9 @@ clean:
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
$(MAKE) -C test check
|
||||
|
||||
.PHONY: upload
|
||||
upload: ${DEB}
|
||||
|
12
test/Makefile
Normal file
12
test/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
SUBDIRS = etc_network_interfaces
|
||||
|
||||
all:
|
||||
|
||||
.PHONY: check install clean distclean
|
||||
|
||||
check:
|
||||
for d in $(SUBDIRS); do $(MAKE) -C $$d check; done
|
||||
|
||||
install: check
|
||||
distclean: clean
|
||||
clean:
|
7
test/etc_network_interfaces/Makefile
Normal file
7
test/etc_network_interfaces/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
all:
|
||||
.PHONY: check install clean distclean
|
||||
install: check
|
||||
clean:
|
||||
distclean: clean
|
||||
check:
|
||||
./runtest.pl
|
16
test/etc_network_interfaces/base
Normal file
16
test/etc_network_interfaces/base
Normal file
@ -0,0 +1,16 @@
|
||||
# network interface settings; autogenerated
|
||||
# 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,
|
||||
# please utilize the 'source' or 'source-directory' directives to do
|
||||
# so.
|
||||
# PVE will preserve these directives, but will NOT its network
|
||||
# configuration from sourced files, so do not attempt to move any of
|
||||
# the PVE managed interfaces into external files!
|
||||
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
iface eth0 inet manual
|
||||
|
14
test/etc_network_interfaces/loopback
Normal file
14
test/etc_network_interfaces/loopback
Normal file
@ -0,0 +1,14 @@
|
||||
# network interface settings; autogenerated
|
||||
# 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,
|
||||
# please utilize the 'source' or 'source-directory' directives to do
|
||||
# so.
|
||||
# PVE will preserve these directives, but will NOT its network
|
||||
# configuration from sourced files, so do not attempt to move any of
|
||||
# the PVE managed interfaces into external files!
|
||||
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
5
test/etc_network_interfaces/proc_net_dev
Normal file
5
test/etc_network_interfaces/proc_net_dev
Normal file
@ -0,0 +1,5 @@
|
||||
Inter-| Receive | Transmit
|
||||
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
||||
vmbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
3
test/etc_network_interfaces/proc_net_if_inet6
Normal file
3
test/etc_network_interfaces/proc_net_if_inet6
Normal file
@ -0,0 +1,3 @@
|
||||
00000000000000000000000000000001 01 80 10 80 lo
|
||||
fe80000000000000ae9e17fffe846a7a 03 40 20 80 vmbr0
|
||||
fc050000000000000000000010000001 03 70 00 80 vmbr0
|
208
test/etc_network_interfaces/runtest.pl
Executable file
208
test/etc_network_interfaces/runtest.pl
Executable file
@ -0,0 +1,208 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use lib '../../src';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Carp;
|
||||
use POSIX;
|
||||
use IO::Handle;
|
||||
|
||||
use PVE::INotify;
|
||||
|
||||
# Current config, r() parses a network interface string into this variable
|
||||
our $config;
|
||||
|
||||
##
|
||||
## Temporary files:
|
||||
##
|
||||
# perl conveniently lets you open a string as filehandle so we allow tests
|
||||
# to temporarily save interface files to virtual files:
|
||||
my %saved_files;
|
||||
|
||||
# Load a temp-file and return it as a string, if it didn't exist, try loading
|
||||
# a real file.
|
||||
sub load($) {
|
||||
my ($from) = @_;
|
||||
|
||||
if (my $local = $saved_files{$from}) {
|
||||
return $local;
|
||||
}
|
||||
|
||||
open my $fh, '<', $from or die "failed to open $from: $!";
|
||||
local $/ = undef;
|
||||
my $data = <$fh>;
|
||||
close $fh;
|
||||
return $data;
|
||||
}
|
||||
|
||||
# Save a temporary file.
|
||||
sub save($$) {
|
||||
my ($file, $data) = @_;
|
||||
$saved_files{$file} = $data;
|
||||
}
|
||||
|
||||
# Delete a temporary file
|
||||
sub delfile($) {
|
||||
my $file = @_;
|
||||
die "no such file: $file" if !delete $saved_files{$file};
|
||||
}
|
||||
|
||||
# Delete all temporary files.
|
||||
sub flush_files() {
|
||||
foreach (keys %saved_files) {
|
||||
delete $saved_files{$_} if $_ !~ m,^shared/,;
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
## Interface parsing:
|
||||
##
|
||||
|
||||
# Read an interfaces file with optional /proc/net/dev and /proc/net/if_inet6
|
||||
# file content strings, which default to the provided ones.
|
||||
sub r($;$$) {
|
||||
my ($ifaces, $proc_net_dev, $proc_net_if_inet6) = @_;
|
||||
$proc_net_dev //= load('proc_net_dev');
|
||||
$proc_net_if_inet6 //= load('proc_net_if_inet6');
|
||||
open my $fh1, '<', \$ifaces;
|
||||
open my $fh2, '<', \$proc_net_dev;
|
||||
open my $fh3, '<', \$proc_net_if_inet6;
|
||||
$config = PVE::INotify::__read_etc_network_interfaces($fh1, $fh2, $fh3);
|
||||
close $fh1;
|
||||
}
|
||||
|
||||
# Turn the current network config into a string.
|
||||
sub w() {
|
||||
return PVE::INotify::__write_etc_network_interfaces($config);
|
||||
}
|
||||
|
||||
##
|
||||
## Interface modification helpers
|
||||
##
|
||||
|
||||
# Update an interface
|
||||
sub update_iface($$%) {
|
||||
my ($name, $families, %extra) = @_;
|
||||
|
||||
my $ifaces = $config->{ifaces};
|
||||
my $if = $ifaces->{$name};
|
||||
|
||||
die "no such interface: $name\n" if !$if;
|
||||
|
||||
$if->{exists} = 1;
|
||||
|
||||
# merge extra flags (like bridge_ports, ovs_*) directly
|
||||
$if->{$_} = $extra{$_} foreach keys %extra;
|
||||
|
||||
return if !$families;
|
||||
|
||||
my $if_families = $if->{families} ||= [];
|
||||
foreach my $family (@$families) {
|
||||
my $type = delete $family->{family};
|
||||
@$if_families = ((grep { $_ ne $type } @$if_families), $type);
|
||||
|
||||
(my $suffix = $type) =~ s/^inet//;
|
||||
$if->{"method$suffix"} = $family->{address} ? 'static' : 'manual';
|
||||
foreach(qw(address netmask gateway options)) {
|
||||
if (my $value = delete $family->{$_}) {
|
||||
$if->{"$_${suffix}"} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create an interface and error if it already exists.
|
||||
sub new_iface($$$%) {
|
||||
my ($name, $type, $families, %extra) = @_;
|
||||
my $ifaces = $config->{ifaces};
|
||||
croak "interface already exists: $name" if $ifaces->{$name};
|
||||
$ifaces->{$name} = { type => $type };
|
||||
update_iface($name, $families, %extra);
|
||||
}
|
||||
|
||||
# Delete an interface and error if it did not exist.
|
||||
sub delete_iface($;$) {
|
||||
my ($name, $family) = @_;
|
||||
my $ifaces = $config->{ifaces};
|
||||
my $if = $ifaces->{$name} ||= {};
|
||||
croak "interface doesn't exist: $name" if !$if;
|
||||
|
||||
if (!$family) {
|
||||
delete $ifaces->{$name};
|
||||
return;
|
||||
}
|
||||
|
||||
my $families = $if->{families};
|
||||
@$families = grep {$_ ne $family} @$families;
|
||||
(my $suffix = $family) =~ s/^inet//;
|
||||
delete $if->{"$_$suffix"} foreach qw(address netmask gateway options);
|
||||
}
|
||||
|
||||
##
|
||||
## Test helpers:
|
||||
##
|
||||
|
||||
# Compare two strings line by line and show a diff/error if they differ.
|
||||
sub diff($$) {
|
||||
my ($a, $b) = @_;
|
||||
return if $a eq $b;
|
||||
|
||||
my ($ra, $wa) = POSIX::pipe();
|
||||
my ($rb, $wb) = POSIX::pipe();
|
||||
my $ha = IO::Handle->new_from_fd($wa, 'w');
|
||||
my $hb = IO::Handle->new_from_fd($wb, 'w');
|
||||
|
||||
open my $diffproc, '-|', 'diff', '-up', "/dev/fd/$ra", "/dev/fd/$rb"
|
||||
or die "failed to run program 'diff': $!";
|
||||
POSIX::close($ra);
|
||||
POSIX::close($rb);
|
||||
|
||||
open my $f1, '<', \$a;
|
||||
open my $f2, '<', \$b;
|
||||
my ($line1, $line2);
|
||||
do {
|
||||
$ha->print($line1) if defined($line1 = <$f1>);
|
||||
$hb->print($line2) if defined($line2 = <$f2>);
|
||||
} while (defined($line1 // $line2));
|
||||
close $f1;
|
||||
close $f2;
|
||||
close $ha;
|
||||
close $hb;
|
||||
|
||||
local $/ = undef;
|
||||
my $diff = <$diffproc>;
|
||||
close $diffproc;
|
||||
die "files differ:\n$diff";
|
||||
}
|
||||
|
||||
# Write the current interface config and compare the result to a string.
|
||||
sub expect($) {
|
||||
my ($expected) = @_;
|
||||
my $got = w();
|
||||
diff($expected, $got);
|
||||
}
|
||||
|
||||
##
|
||||
## Main test execution:
|
||||
##
|
||||
# (sorted, it's not used right now but tests could pass on temporary files by
|
||||
# prefixing the name with shared/ and thus you might want to split a larger
|
||||
# test into t.01.first-part.pl, t.02.second-part.pl, etc.
|
||||
my $total = 0;
|
||||
my $failed = 0;
|
||||
for our $Test (sort <t.*.pl>) {
|
||||
$total++;
|
||||
flush_files();
|
||||
eval {
|
||||
require $Test;
|
||||
};
|
||||
if ($@) {
|
||||
print "FAIL: $Test\n$@\n\n";
|
||||
$failed++;
|
||||
} else {
|
||||
print "PASS: $Test\n";
|
||||
}
|
||||
}
|
||||
|
||||
die "$failed out of $total tests failed\n" if $failed;
|
12
test/etc_network_interfaces/t.base.pl
Normal file
12
test/etc_network_interfaces/t.base.pl
Normal file
@ -0,0 +1,12 @@
|
||||
my $wanted = load('base');
|
||||
|
||||
# parse the empty file
|
||||
r('');
|
||||
expect $wanted;
|
||||
|
||||
# idempotency
|
||||
# save, re-parse, and re-check
|
||||
r(w());
|
||||
expect $wanted;
|
||||
|
||||
1;
|
84
test/etc_network_interfaces/t.bridge-v4-v6.pl
Normal file
84
test/etc_network_interfaces/t.bridge-v4-v6.pl
Normal file
@ -0,0 +1,84 @@
|
||||
my $ip = '10.0.0.2';
|
||||
my $nm = '255.255.255.0';
|
||||
my $gw = '10.0.0.1';
|
||||
my $ip6 = 'fc05::1:2';
|
||||
my $nm6 = '112';
|
||||
my $gw6 = 'fc05::1:1';
|
||||
|
||||
r(load('base'));
|
||||
|
||||
new_iface('vmbr0', 'bridge', [{ family => 'inet' }], autostart => 1, bridge_ports => 'eth0');
|
||||
|
||||
expect load('base') . <<"EOF";
|
||||
auto vmbr0
|
||||
iface vmbr0 inet manual
|
||||
bridge_ports eth0
|
||||
bridge_stp off
|
||||
bridge_fd 0
|
||||
|
||||
EOF
|
||||
|
||||
# add an ip and disable previously enabled autostart
|
||||
update_iface('vmbr0',
|
||||
[ { family => 'inet',
|
||||
address => $ip,
|
||||
netmask => $nm,
|
||||
gateway => $gw } ],
|
||||
autostart => 0);
|
||||
|
||||
expect load('base') . <<"EOF";
|
||||
iface vmbr0 inet static
|
||||
address $ip
|
||||
netmask $nm
|
||||
gateway $gw
|
||||
bridge_ports eth0
|
||||
bridge_stp off
|
||||
bridge_fd 0
|
||||
|
||||
EOF
|
||||
save('with-ipv4', w());
|
||||
|
||||
update_iface('vmbr0',
|
||||
[ { family => 'inet6',
|
||||
address => $ip6,
|
||||
netmask => $nm6,
|
||||
gateway => $gw6 } ]);
|
||||
|
||||
expect load('with-ipv4') . <<"EOF";
|
||||
iface vmbr0 inet6 static
|
||||
address $ip6
|
||||
netmask $nm6
|
||||
gateway $gw6
|
||||
|
||||
EOF
|
||||
|
||||
# idempotency
|
||||
save('idem', w());
|
||||
r(load('idem'));
|
||||
expect load('idem');
|
||||
|
||||
# delete vmbr0's inet
|
||||
delete_iface('vmbr0', 'inet');
|
||||
|
||||
# bridge ports must now appear in the inet6 block
|
||||
expect load('base') . <<"EOF";
|
||||
iface vmbr0 inet6 static
|
||||
address $ip6
|
||||
netmask $nm6
|
||||
gateway $gw6
|
||||
bridge_ports eth0
|
||||
bridge_stp off
|
||||
bridge_fd 0
|
||||
|
||||
EOF
|
||||
|
||||
# idempotency
|
||||
save('idem', w());
|
||||
r(load('idem'));
|
||||
expect load('idem');
|
||||
|
||||
# delete vmbr0 completely
|
||||
delete_iface('vmbr0');
|
||||
expect load('base');
|
||||
|
||||
1;
|
28
test/etc_network_interfaces/t.keep-option-order.pl
Normal file
28
test/etc_network_interfaces/t.keep-option-order.pl
Normal file
@ -0,0 +1,28 @@
|
||||
#
|
||||
# Order of option lines between interfaces should be preserved:
|
||||
# eth0 is unconfigured and will thus end up at the end as 'manual'
|
||||
#
|
||||
my $ordered = <<'ORDERED';
|
||||
source /etc/network/config1
|
||||
|
||||
iface eth1 inet manual
|
||||
|
||||
source-directory /etc/network/interfaces.d
|
||||
|
||||
iface eth2 inet manual
|
||||
|
||||
iface eth3 inet manual
|
||||
|
||||
ORDERED
|
||||
|
||||
r("$ordered", <<'/proc/net/dev'
|
||||
eth0:
|
||||
eth1:
|
||||
eth2:
|
||||
eth3:
|
||||
/proc/net/dev
|
||||
);
|
||||
|
||||
expect(load('loopback') . $ordered . "iface eth0 inet manual\n\n");
|
||||
|
||||
1;
|
116
test/etc_network_interfaces/t.ovs_bridge_allow.pl
Normal file
116
test/etc_network_interfaces/t.ovs_bridge_allow.pl
Normal file
@ -0,0 +1,116 @@
|
||||
use strict;
|
||||
|
||||
my $ip = '192.168.0.100';
|
||||
my $nm = '255.255.255.0';
|
||||
my $gw = '192.168.0.1';
|
||||
|
||||
# replace proc_net_dev with one with a bunch of interfaces
|
||||
save('proc_net_dev', <<'/proc/net/dev');
|
||||
eth0:
|
||||
eth1:
|
||||
eth2:
|
||||
eth3:
|
||||
/proc/net/dev
|
||||
|
||||
r('');
|
||||
|
||||
new_iface('vmbr0', 'OVSBridge',
|
||||
[ { family => 'inet',
|
||||
address => $ip,
|
||||
netmask => $nm,
|
||||
gateway => $gw } ],
|
||||
autostart => 1);
|
||||
|
||||
update_iface('eth0', [], autostart => 1);
|
||||
update_iface('eth1', [], autostart => 1);
|
||||
update_iface('eth2', [], autostart => 1);
|
||||
#update_iface('eth3', [], autostart => 1);
|
||||
|
||||
# Check the bridge and eth interfaces
|
||||
expect load('loopback') . <<"/etc/network/interfaces";
|
||||
auto eth0
|
||||
iface eth0 inet manual
|
||||
|
||||
auto eth1
|
||||
iface eth1 inet manual
|
||||
|
||||
auto eth2
|
||||
iface eth2 inet manual
|
||||
|
||||
iface eth3 inet manual
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address $ip
|
||||
netmask $nm
|
||||
gateway $gw
|
||||
ovs_type OVSBridge
|
||||
|
||||
/etc/network/interfaces
|
||||
|
||||
# Adding an interface to the bridge needs to add allow- lines:
|
||||
update_iface('vmbr0', [], ovs_ports => 'eth1 eth2');
|
||||
expect load('loopback') . <<"/etc/network/interfaces";
|
||||
auto eth0
|
||||
iface eth0 inet manual
|
||||
|
||||
auto eth1
|
||||
allow-vmbr0 eth1
|
||||
iface eth1 inet manual
|
||||
ovs_type OVSPort
|
||||
ovs_bridge vmbr0
|
||||
|
||||
auto eth2
|
||||
allow-vmbr0 eth2
|
||||
iface eth2 inet manual
|
||||
ovs_type OVSPort
|
||||
ovs_bridge vmbr0
|
||||
|
||||
iface eth3 inet manual
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address $ip
|
||||
netmask $nm
|
||||
gateway $gw
|
||||
ovs_type OVSBridge
|
||||
ovs_ports eth1 eth2
|
||||
|
||||
/etc/network/interfaces
|
||||
|
||||
# Idempotency - make sure "allow-$BRIDGE $IFACE" don't get duplicated
|
||||
# they're stripped from $config->{options} at load-time since they're
|
||||
# auto-generated when writing OVSPorts.
|
||||
save('idem', w());
|
||||
r(load('idem'));
|
||||
expect load('idem');
|
||||
|
||||
# Removing an ovs_port also has to remove the corresponding allow- line!
|
||||
# Also remember that adding interfaces to the ovs bridge removed their
|
||||
# autostart property, so eth2 is now without an autostart!
|
||||
update_iface('vmbr0', [], ovs_ports => 'eth1');
|
||||
# eth2 is now autoremoved and thus loses its priority, so it appears after eth3
|
||||
expect load('loopback') . <<"/etc/network/interfaces";
|
||||
auto eth0
|
||||
iface eth0 inet manual
|
||||
|
||||
allow-vmbr0 eth1
|
||||
iface eth1 inet manual
|
||||
ovs_type OVSPort
|
||||
ovs_bridge vmbr0
|
||||
|
||||
iface eth3 inet manual
|
||||
|
||||
iface eth2 inet manual
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address $ip
|
||||
netmask $nm
|
||||
gateway $gw
|
||||
ovs_type OVSBridge
|
||||
ovs_ports eth1
|
||||
|
||||
/etc/network/interfaces
|
||||
|
||||
1;
|
@ -0,0 +1,25 @@
|
||||
r('', <<'/proc/net/dev'
|
||||
Inter-| Receive | Transmit
|
||||
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
||||
These eth interfaces show up:
|
||||
eth0:
|
||||
eth1:
|
||||
eth2:
|
||||
eth3:
|
||||
lo:
|
||||
All other stuff is being ignored eth99:
|
||||
eth100 is not actually available:
|
||||
ethBAD: this one's now allowed either
|
||||
/proc/net/dev
|
||||
);
|
||||
|
||||
expect load('base') . <<'IFACES';
|
||||
iface eth1 inet manual
|
||||
|
||||
iface eth2 inet manual
|
||||
|
||||
iface eth3 inet manual
|
||||
|
||||
IFACES
|
||||
|
||||
1;
|
Loading…
Reference in New Issue
Block a user