5
0
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:
Wolfgang Bumiller 2015-06-25 11:54:31 +02:00 committed by Dietmar Maurer
parent 516a50a4ac
commit 936218b86f
13 changed files with 533 additions and 0 deletions

View File

@ -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
View 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:

View File

@ -0,0 +1,7 @@
all:
.PHONY: check install clean distclean
install: check
clean:
distclean: clean
check:
./runtest.pl

View 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

View 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

View 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

View File

@ -0,0 +1,3 @@
00000000000000000000000000000001 01 80 10 80 lo
fe80000000000000ae9e17fffe846a7a 03 40 20 80 vmbr0
fc050000000000000000000010000001 03 70 00 80 vmbr0

View 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;

View 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;

View 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;

View 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;

View 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;

View File

@ -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;