[pve-devel] [PATCH pve-network 4/5] add etc_network_interfaces tests (moved from pve-common)

Alexandre Derumier aderumier at odiso.com
Fri Jun 7 11:58:31 CEST 2019


Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 test/Makefile                                 |  12 +
 test/etc_network_interfaces/Makefile          |   7 +
 test/etc_network_interfaces/active_interfaces |   3 +
 test/etc_network_interfaces/base              |  16 +
 test/etc_network_interfaces/brbase            |  26 ++
 test/etc_network_interfaces/loopback          |  14 +
 test/etc_network_interfaces/proc_net_dev      |   5 +
 test/etc_network_interfaces/runtest.pl        | 208 +++++++++
 test/etc_network_interfaces/t.base.pl         |  12 +
 test/etc_network_interfaces/t.bridge-v4-v6.pl |  84 ++++
 .../t.bridge_eth_remove_auto.pl               |  24 +
 .../t.create_network.pl                       | 424 ++++++++++++++++++
 .../t.keep-option-order.pl                    |  28 ++
 .../t.list-interfaces.pl                      | 109 +++++
 .../t.ovs_bridge_allow.pl                     | 115 +++++
 .../t.parsed_options.pl                       |  35 ++
 .../t.unhandled-interfaces-to-manual.pl       |  25 ++
 .../etc_network_interfaces/t.unknown_order.pl | 102 +++++
 .../t.update_network.pl                       | 116 +++++
 19 files changed, 1365 insertions(+)
 create mode 100644 test/Makefile
 create mode 100644 test/etc_network_interfaces/Makefile
 create mode 100644 test/etc_network_interfaces/active_interfaces
 create mode 100644 test/etc_network_interfaces/base
 create mode 100644 test/etc_network_interfaces/brbase
 create mode 100644 test/etc_network_interfaces/loopback
 create mode 100644 test/etc_network_interfaces/proc_net_dev
 create mode 100755 test/etc_network_interfaces/runtest.pl
 create mode 100644 test/etc_network_interfaces/t.base.pl
 create mode 100644 test/etc_network_interfaces/t.bridge-v4-v6.pl
 create mode 100644 test/etc_network_interfaces/t.bridge_eth_remove_auto.pl
 create mode 100644 test/etc_network_interfaces/t.create_network.pl
 create mode 100644 test/etc_network_interfaces/t.keep-option-order.pl
 create mode 100644 test/etc_network_interfaces/t.list-interfaces.pl
 create mode 100644 test/etc_network_interfaces/t.ovs_bridge_allow.pl
 create mode 100644 test/etc_network_interfaces/t.parsed_options.pl
 create mode 100644 test/etc_network_interfaces/t.unhandled-interfaces-to-manual.pl
 create mode 100644 test/etc_network_interfaces/t.unknown_order.pl
 create mode 100644 test/etc_network_interfaces/t.update_network.pl

diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..82851cb
--- /dev/null
+++ b/test/Makefile
@@ -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:
diff --git a/test/etc_network_interfaces/Makefile b/test/etc_network_interfaces/Makefile
new file mode 100644
index 0000000..aeb4141
--- /dev/null
+++ b/test/etc_network_interfaces/Makefile
@@ -0,0 +1,7 @@
+all:
+.PHONY: check install clean distclean
+install: check
+clean:
+distclean: clean
+check:
+	./runtest.pl
diff --git a/test/etc_network_interfaces/active_interfaces b/test/etc_network_interfaces/active_interfaces
new file mode 100644
index 0000000..dd3634d
--- /dev/null
+++ b/test/etc_network_interfaces/active_interfaces
@@ -0,0 +1,3 @@
+lo
+eth0
+vmbr0
diff --git a/test/etc_network_interfaces/base b/test/etc_network_interfaces/base
new file mode 100644
index 0000000..bb76f55
--- /dev/null
+++ b/test/etc_network_interfaces/base
@@ -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 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 read 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
+
diff --git a/test/etc_network_interfaces/brbase b/test/etc_network_interfaces/brbase
new file mode 100644
index 0000000..72a8740
--- /dev/null
+++ b/test/etc_network_interfaces/brbase
@@ -0,0 +1,26 @@
+# network interface settings; autogenerated
+# Please do NOT modify this file directly, unless you know what
+# you're doing.
+#
+# 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 read 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
+
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge_ports eth0
+	bridge_stp off
+	bridge_fd 0
diff --git a/test/etc_network_interfaces/loopback b/test/etc_network_interfaces/loopback
new file mode 100644
index 0000000..2c6cb10
--- /dev/null
+++ b/test/etc_network_interfaces/loopback
@@ -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 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 read 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
+
diff --git a/test/etc_network_interfaces/proc_net_dev b/test/etc_network_interfaces/proc_net_dev
new file mode 100644
index 0000000..01df936
--- /dev/null
+++ b/test/etc_network_interfaces/proc_net_dev
@@ -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
diff --git a/test/etc_network_interfaces/runtest.pl b/test/etc_network_interfaces/runtest.pl
new file mode 100755
index 0000000..d9efdde
--- /dev/null
+++ b/test/etc_network_interfaces/runtest.pl
@@ -0,0 +1,208 @@
+#!/usr/bin/perl
+
+use lib '../../src';
+use lib '.';
+use strict;
+use warnings;
+
+use Carp;
+use POSIX;
+use IO::Handle;
+
+use PVE::NetworkConfig;
+
+# 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 file content string and
+# the list of active interfaces, which otherwise default
+sub r($;$$) {
+    my ($ifaces, $proc_net_dev, $active) = @_;
+    $proc_net_dev //= load('proc_net_dev');
+    $active //= [split(/\s+/, load('active_interfaces'))];
+    open my $fh1, '<', \$ifaces;
+    open my $fh2, '<', \$proc_net_dev;
+    $config = PVE::NetworkConfig::__read_etc_network_interfaces($fh1, $fh2, $active);
+    close $fh1;
+}
+
+# Turn the current network config into a string.
+sub w() {
+    return PVE::NetworkConfig::__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;
diff --git a/test/etc_network_interfaces/t.base.pl b/test/etc_network_interfaces/t.base.pl
new file mode 100644
index 0000000..9980a2c
--- /dev/null
+++ b/test/etc_network_interfaces/t.base.pl
@@ -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;
diff --git a/test/etc_network_interfaces/t.bridge-v4-v6.pl b/test/etc_network_interfaces/t.bridge-v4-v6.pl
new file mode 100644
index 0000000..7818126
--- /dev/null
+++ b/test/etc_network_interfaces/t.bridge-v4-v6.pl
@@ -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;
diff --git a/test/etc_network_interfaces/t.bridge_eth_remove_auto.pl b/test/etc_network_interfaces/t.bridge_eth_remove_auto.pl
new file mode 100644
index 0000000..98f5df8
--- /dev/null
+++ b/test/etc_network_interfaces/t.bridge_eth_remove_auto.pl
@@ -0,0 +1,24 @@
+use strict;
+
+# access to the current config
+our $config;
+
+# replace proc_net_dev with one with a bunch of interfaces
+save('proc_net_dev', <<'/proc/net/dev');
+eth0:
+eth1:
+/proc/net/dev
+
+r('');
+update_iface('eth0', [], autostart => 1);
+update_iface('eth1', [], autostart => 1);
+r(w());
+die "autostart lost" if !$config->{ifaces}->{eth0}->{autostart};
+die "autostart lost" if !$config->{ifaces}->{eth1}->{autostart};
+new_iface("vmbr0", 'bridge', [{ family => 'inet' }], bridge_ports => 'eth0');
+new_iface("vmbr1", 'OVSBridge', [{ family => 'inet' }], ovs_ports => 'eth1');
+r(w());
+die "autostart wrongly removed for linux bridge port" if !$config->{ifaces}->{eth0}->{autostart};
+die "autostart not removed for ovs bridge port" if $config->{ifaces}->{eth1}->{autostart};
+
+1;
diff --git a/test/etc_network_interfaces/t.create_network.pl b/test/etc_network_interfaces/t.create_network.pl
new file mode 100644
index 0000000..bf5b4b6
--- /dev/null
+++ b/test/etc_network_interfaces/t.create_network.pl
@@ -0,0 +1,424 @@
+save('proc_net_dev', <<'/proc/net/dev');
+eth0:
+eth1:
+eth2:
+eth3:
+/proc/net/dev
+
+r(load('brbase'));
+
+#
+# Variables used for the various interfaces:
+#
+
+my $ip = '192.168.0.2';
+my $nm = '255.255.255.0';
+my $gw = '192.168.0.1';
+my $svcnodeip = '239.192.105.237';
+my $physdev = 'eth0';
+my $remoteip1 = '192.168.0.3';
+my $remoteip2 = '192.168.0.4';
+
+#
+# Hunk for the default bridge of the 'brbase' configuration
+#
+
+my $vmbr0_part = <<"PART";
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge-ports eth0
+	bridge-stp off
+	bridge-fd 0
+PART
+chomp $vmbr0_part;
+
+#
+# Configure eth1 statically, store its expected interfaces hunk in $eth1_part
+# and test!
+#
+
+$config->{ifaces}->{eth1} = {
+    type => 'eth',
+    method => 'static',
+    address => $ip,
+    netmask => $nm,
+    gateway => $gw,
+    families => ['inet'],
+    autostart => 1
+};
+
+my $eth1_part = <<"PART";
+auto eth1
+iface eth1 inet static
+	address  $ip
+	netmask  $nm
+	gateway  $gw
+PART
+chomp $eth1_part;
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+$eth1_part
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+$vmbr0_part
+
+CHECK
+
+#
+# Add a bond for eth2 & 3 and check the new output
+#
+
+$config->{ifaces}->{bond0} = {
+    type => 'bond',
+    mtu => 1400,
+    slaves => 'eth2 eth3',
+    bond_mode => '802.3ad',
+    bond_xmit_hash_policy => 'layer3+4',
+    bond_miimon => 100,
+    method => 'manual',
+    families => ['inet'],
+    autostart => 1
+};
+my $bond0_part = <<"PART";
+auto bond0
+iface bond0 inet manual
+	bond-slaves eth2 eth3
+	bond-miimon 100
+	bond-mode 802.3ad
+	bond-xmit-hash-policy layer3+4
+	mtu 1400
+PART
+chomp $bond0_part;
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+$eth1_part
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+$bond0_part
+
+$vmbr0_part
+
+CHECK
+
+#
+# Add vxlan1 and 2
+#
+
+$config->{ifaces}->{vxlan1} = {
+    type => 'vxlan',
+    method => 'manual',
+    families => ['inet'],
+    'vxlan-id' => 1,
+    'vxlan-svcnodeip' => $svcnodeip,
+    'vxlan-physdev' => $physdev,
+    autostart => 1
+};
+
+$config->{ifaces}->{vxlan2} = {
+    type => 'vxlan',
+    method => 'manual',
+    families => ['inet'],
+    'vxlan-id' => 2,
+    'vxlan-local-tunnelip' => $ip,
+    autostart => 1
+};
+
+my $vxlan12_part = <<"PART";
+auto vxlan1
+iface vxlan1 inet manual
+	vxlan-id 1
+	vxlan-svcnodeip $svcnodeip
+	vxlan-physdev $physdev
+
+auto vxlan2
+iface vxlan2 inet manual
+	vxlan-id 2
+	vxlan-local-tunnelip $ip
+PART
+chomp $vxlan12_part;
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+$eth1_part
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+$bond0_part
+
+$vmbr0_part
+
+$vxlan12_part
+
+CHECK
+
+#
+# Add vxlan3 and 3 bridges using vxlan1..3
+#
+
+$config->{ifaces}->{vmbr1} = {
+    mtu => 1400,
+    type => 'bridge',
+    method => 'manual',
+    families => ['inet'],
+    bridge_stp => 'off',
+    bridge_fd => 0,
+    bridge_ports => 'vxlan1',
+    bridge_vlan_aware => 'yes',
+    autostart => 1
+};
+
+$config->{ifaces}->{vmbr2} = {
+    type => 'bridge',
+    method => 'manual',
+    families => ['inet'],
+    bridge_stp => 'off',
+    bridge_fd => 0,
+    bridge_ports => 'vxlan2',
+    autostart => 1
+};
+
+$config->{ifaces}->{vmbr3} = {
+    type => 'bridge',
+    method => 'manual',
+    families => ['inet'],
+    bridge_stp => 'off',
+    bridge_fd => 0,
+    bridge_ports => 'vxlan3.50',
+    bridge_vlan_aware => 'yes',
+    bridge_vids => '2-10',
+    autostart => 1
+};
+
+my $vmbr123_part = <<"PART";
+auto vmbr1
+iface vmbr1 inet manual
+	bridge-ports vxlan1
+	bridge-stp off
+	bridge-fd 0
+	bridge-vlan-aware yes
+	bridge-vids 2-4094
+	mtu 1400
+
+auto vmbr2
+iface vmbr2 inet manual
+	bridge-ports vxlan2
+	bridge-stp off
+	bridge-fd 0
+
+auto vmbr3
+iface vmbr3 inet manual
+	bridge-ports vxlan3.50
+	bridge-stp off
+	bridge-fd 0
+	bridge-vlan-aware yes
+	bridge-vids 2-10
+PART
+chomp $vmbr123_part;
+
+$config->{ifaces}->{vxlan3} = {
+    type => 'vxlan',
+    method => 'manual',
+    families => ['inet'],
+    'vxlan-id' => 3,
+    'vxlan-remoteip' => [$remoteip1, $remoteip2],
+    'bridge-access' => 3,
+    autostart => 1
+};
+
+my $vx = $config->{ifaces}->{vxlan2};
+$vx->{'bridge-learning'} = 'off';
+$vx->{'bridge-arp-nd-suppress'} = 'on';
+$vx->{'bridge-unicast-flood'} = 'off';
+$vx->{'bridge-multicast-flood'} = 'off';
+my $vxlan123_part = $vxlan12_part ."\n" . <<"PART";
+	bridge-arp-nd-suppress on
+	bridge-learning off
+	bridge-multicast-flood off
+	bridge-unicast-flood off
+
+auto vxlan3
+iface vxlan3 inet manual
+	vxlan-id 3
+	vxlan-remoteip $remoteip1
+	vxlan-remoteip $remoteip2
+	bridge-access 3
+PART
+chomp $vxlan123_part;
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+$eth1_part
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+$bond0_part
+
+$vmbr0_part
+
+$vmbr123_part
+
+$vxlan123_part
+
+CHECK
+
+#
+# Now add vlans on all types of interfaces: vmbr1, bond0 and eth1
+#
+
+$config->{ifaces}->{'vmbr1.100'} = {
+    type => 'vlan',
+    mtu => 1300,
+    method => 'manual',
+    families => ['inet'],
+    autostart => 1
+};
+
+$config->{ifaces}->{'bond0.100'} = {
+    type => 'vlan',
+    mtu => 1300,
+    method => 'manual',
+    families => ['inet'],
+    'vlan-protocol' => '802.1ad',
+    autostart => 1
+};
+
+$config->{ifaces}->{'bond0.100.10'} = {
+    type => 'vlan',
+    mtu => 1300,
+    method => 'manual',
+    families => ['inet'],
+    autostart => 1
+};
+
+$config->{ifaces}->{'eth1.100'} = {
+    type => 'vlan',
+    mtu => 1400,
+    method => 'manual',
+    families => ['inet'],
+    autostart => 1
+};
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+$eth1_part
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+auto eth1.100
+iface eth1.100 inet manual
+	mtu 1400
+
+$bond0_part
+
+auto bond0.100
+iface bond0.100 inet manual
+	mtu 1300
+	vlan-protocol 802.1ad
+
+auto bond0.100.10
+iface bond0.100.10 inet manual
+	mtu 1300
+
+$vmbr0_part
+
+$vmbr123_part
+
+auto vmbr1.100
+iface vmbr1.100 inet manual
+	mtu 1300
+
+$vxlan123_part
+
+CHECK
+
+#
+# Now check the new config for idempotency:
+#
+
+save('if', w());
+r(load('if'));
+expect load('if');
+
+#
+# Check a brbase with an ipv6 address on eth1
+#
+
+r(load('brbase'));
+
+my $ip = 'fc05::2';
+my $nm = '112';
+my $gw = 'fc05::1';
+
+$config->{ifaces}->{eth1} = {
+    type => 'eth',
+    method6 => 'static',
+    address6 => $ip,
+    netmask6 => $nm,
+    gateway6 => $gw,
+    families => ['inet6'],
+    autostart => 1
+};
+
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+auto eth1
+iface eth1 inet6 static
+	address  $ip
+	netmask  $nm
+	gateway  $gw
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge-ports eth0
+	bridge-stp off
+	bridge-fd 0
+
+CHECK
+
+save('if', w());
+r(load('if'));
+expect load('if');
+
+1;
diff --git a/test/etc_network_interfaces/t.keep-option-order.pl b/test/etc_network_interfaces/t.keep-option-order.pl
new file mode 100644
index 0000000..d1e07a8
--- /dev/null
+++ b/test/etc_network_interfaces/t.keep-option-order.pl
@@ -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;
diff --git a/test/etc_network_interfaces/t.list-interfaces.pl b/test/etc_network_interfaces/t.list-interfaces.pl
new file mode 100644
index 0000000..41907ae
--- /dev/null
+++ b/test/etc_network_interfaces/t.list-interfaces.pl
@@ -0,0 +1,109 @@
+# Assuming eth0..3 and eth100
+# eth0 is part of vmbr0, eth100 is part of the OVS bridge vmbr1
+# vmbr0 has ipv4 and ipv6, OVS only ipv4
+#
+# eth1..3 are completely un-configured as if the cards had just been physically
+# plugged in.
+# The expected behavior is to notice their existance and treat them as manually
+# configured interfaces.
+# Saving the file after reading would add the corresponding 'manual' lines.
+save('proc_net_dev', <<'/proc/net/dev');
+eth0:
+eth1:
+eth2:
+eth3:
+eth100:
+/proc/net/dev
+
+my %wanted = (
+    vmbr0 => { address => '192.168.1.2',
+	       netmask => '255.255.255.0',
+	       gateway => '192.168.1.1',
+	       address6 => 'fc05::1:1',
+	       netmask6 => '112' },
+    vmbr1 => { address => '10.0.0.5',
+	       netmask => '255.255.255.0' }
+);
+
+save('interfaces', <<"/etc/network/interfaces");
+auto lo
+iface lo inet loopback
+
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+allow-vmbr1 eth100
+iface eth100 inet manual
+	ovs_type OVSPort
+	ovs_bridge vmbr1
+
+auto vmbr0
+iface vmbr0 inet static
+	address  $wanted{vmbr0}->{address}
+	netmask  $wanted{vmbr0}->{netmask}
+	gateway  $wanted{vmbr0}->{gateway}
+	bridge_ports eth0
+	bridge_stp off
+	bridge_fd 0
+
+iface vmbr0 inet6 static
+	address  $wanted{vmbr0}->{address6}
+	netmask  $wanted{vmbr0}->{netmask6}
+
+source-directory before-ovs.d
+
+auto vmbr1
+iface vmbr1 inet static
+	address  $wanted{vmbr1}->{address}
+	netmask  $wanted{vmbr1}->{netmask}
+	ovs_type OVSBridge
+	ovs_ports eth100
+
+source after-ovs
+
+/etc/network/interfaces
+
+r(load('interfaces'));
+save('2', w());
+
+my $ifaces = $config->{ifaces};
+
+# check defined interfaces
+defined($ifaces->{"eth$_"})
+    or die "missing interface: eth$_\n" foreach (0, 1, 2, 3, 100);
+
+# check configuration
+foreach my $ifname (keys %wanted) {
+    my $if = $wanted{$ifname};
+    $ifaces->{$ifname}->{$_} eq $if->{$_}
+	or die "unexpected $_ for interface $ifname: \""
+	     . $ifaces->{$ifname}->{$_}
+	     . "\", expected: \"$if->{$_}\"\n"
+	foreach (keys %$if);
+}
+
+my $ck = sub {
+    my ($i, $v, $e) = @_;
+    $ifaces->{$i}->{$v} eq $e
+	or die "$i variable $v: got \"$ifaces->{$i}->{$v}\", expected: $e\n";
+};
+$ck->('vmbr0', type => 'bridge');
+$ck->('vmbr1', type => 'OVSBridge');
+$ck->('vmbr1', ovs_type => 'OVSBridge');
+$ck->('vmbr1', ovs_ports => 'eth100');
+$ck->("eth$_", type => 'eth') foreach (0, 1, 2, 3);
+$ck->('eth100', type => 'OVSPort');
+$ck->('eth100', ovs_type => 'OVSPort');
+$ck->('eth100', ovs_bridge => 'vmbr1');
+
+my @f100 = sort @{$ifaces->{vmbr0}->{families}};
+
+die "invalid families defined for vmbr0"
+    if (scalar(@f100) != 2) || ($f100[0] ne 'inet') || ($f100[1] ne 'inet6');
+ 
+# idempotency
+r(w());
+expect load('2');
+
+1;
diff --git a/test/etc_network_interfaces/t.ovs_bridge_allow.pl b/test/etc_network_interfaces/t.ovs_bridge_allow.pl
new file mode 100644
index 0000000..6d22243
--- /dev/null
+++ b/test/etc_network_interfaces/t.ovs_bridge_allow.pl
@@ -0,0 +1,115 @@
+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 and remove
+# its autostart property.
+update_iface('vmbr0', [], ovs_ports => 'eth1 eth2');
+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
+
+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;
diff --git a/test/etc_network_interfaces/t.parsed_options.pl b/test/etc_network_interfaces/t.parsed_options.pl
new file mode 100644
index 0000000..40b5b11
--- /dev/null
+++ b/test/etc_network_interfaces/t.parsed_options.pl
@@ -0,0 +1,35 @@
+save('proc_net_dev', <<'/proc/net/dev');
+eth0:
+eth1:
+/proc/net/dev
+
+# Check for dropped or duplicated options
+
+my $ip = '192.168.0.2';
+my $nm = '255.255.255.0';
+my $gw = '192.168.0.1';
+my $ip6 = 'fc05::2';
+my $nm6 = '112';
+my $gw6 = 'fc05::1';
+
+# Load
+my $cfg = load('base') . <<"CHECK";
+iface eth1 inet manual
+
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge-ports eth0
+	bridge-stp off
+	bridge-fd 0
+	bridge-vlan-aware yes
+	bridge-vids 2-4094
+
+CHECK
+
+r $cfg;
+expect $cfg;
+
+1;
diff --git a/test/etc_network_interfaces/t.unhandled-interfaces-to-manual.pl b/test/etc_network_interfaces/t.unhandled-interfaces-to-manual.pl
new file mode 100644
index 0000000..6c77e33
--- /dev/null
+++ b/test/etc_network_interfaces/t.unhandled-interfaces-to-manual.pl
@@ -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;
diff --git a/test/etc_network_interfaces/t.unknown_order.pl b/test/etc_network_interfaces/t.unknown_order.pl
new file mode 100644
index 0000000..a4346ca
--- /dev/null
+++ b/test/etc_network_interfaces/t.unknown_order.pl
@@ -0,0 +1,102 @@
+my $base = load('loopback');
+sub wanted($) {
+    my ($ip) = @_;
+    return $base . <<"IFACES";
+iface eth0 inet manual
+
+iface eth1 inet manual
+
+iface eth2 inet manual
+
+iface eth3 inet manual
+
+iface eth4 inet manual
+
+iface eth5 inet manual
+
+iface eth6 inet manual
+
+iface eth7 inet manual
+
+iface bond0 inet manual
+	bond-slaves eth0 eth1
+	bond-miimon 100
+	bond-mode balance-alb
+
+auto bond1
+iface bond1 inet static
+	address  10.10.10.$ip
+	netmask  255.255.255.0
+	bond-slaves eth2 eth3
+	bond-miimon 100
+	bond-mode balance-alb
+#       pre-up ifconfig bond1 mtu 9000
+
+auto bond2
+iface bond2 inet manual
+	bond-slaves eth4 eth5
+	bond-miimon 100
+	bond-mode balance-alb
+# Private networking
+
+iface vlan3 inet static
+	address  0.0.0.0
+	netmask  0.0.0.0
+	vlan-raw-device bond2
+
+iface vlan4 inet static
+	address  0.0.0.0
+	netmask  0.0.0.0
+	vlan-raw-device bond2
+
+iface vlan5 inet static
+	address  0.0.0.0
+	netmask  0.0.0.0
+	vlan-raw-device bond2
+
+auto vmbr0
+iface vmbr0 inet static
+	address  192.168.100.13
+	netmask  255.255.255.0
+	gateway  192.168.100.1
+	bridge-ports bond0
+	bridge-stp off
+	bridge-fd 0
+
+auto vlan6
+iface vlan6 inet static
+	address  10.10.11.13
+	netmask  255.255.255.0
+	vlan_raw_device bond0
+	network 10.10.11.0
+	pre-up ifconfig bond0 up
+
+auto vmbr3
+iface vmbr3 inet manual
+	bridge-ports vlan3
+	bridge-stp off
+	bridge-fd 0
+	pre-up ifup vlan3
+
+auto vmbr4
+iface vmbr4 inet manual
+	bridge-ports vlan4
+	bridge-stp off
+	bridge-fd 0
+	pre-up ifup vlan4
+
+auto vmbr5
+iface vmbr5 inet manual
+	bridge-ports vlan5
+	bridge-stp off
+	bridge-fd 0
+	pre-up ifup vlan5
+
+IFACES
+}
+
+r(wanted(13));
+update_iface('bond1', [ { family => 'inet', address => '10.10.10.11' } ]);
+expect wanted(11);
+
+1;
diff --git a/test/etc_network_interfaces/t.update_network.pl b/test/etc_network_interfaces/t.update_network.pl
new file mode 100644
index 0000000..4bf70cf
--- /dev/null
+++ b/test/etc_network_interfaces/t.update_network.pl
@@ -0,0 +1,116 @@
+save('proc_net_dev', <<'/proc/net/dev');
+eth0:
+eth1:
+/proc/net/dev
+
+my $ip = '192.168.0.2';
+my $nm = '255.255.255.0';
+my $gw = '192.168.0.1';
+my $ip6 = 'fc05::2';
+my $nm6 = '112';
+my $gw6 = 'fc05::1';
+
+# Load
+r(load('brbase'));
+
+# Create eth1
+$config->{ifaces}->{eth1} = {
+    type => 'eth',
+    method => 'static',
+    address => $ip,
+    netmask => $nm,
+    gateway => $gw,
+    families => ['inet'],
+    autostart => 1
+};
+
+# Check
+expect load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+auto eth1
+iface eth1 inet static
+	address  $ip
+	netmask  $nm
+	gateway  $gw
+
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge-ports eth0
+	bridge-stp off
+	bridge-fd 0
+
+CHECK
+
+# Reload then modify
+save('ipv4', w());
+r(load('ipv4'));
+expect load('ipv4');
+
+$config->{ifaces}->{eth1}->{$_->[0]} = $_->[1] foreach (
+    [ method6 => 'static' ],
+    [ address6 => $ip6 ],
+    [ netmask6 => $nm6 ],
+    [ gateway6 => $gw6 ],
+    [ families => ['inet', 'inet6'] ]
+);
+
+# Check
+my $final = load('loopback') . <<"CHECK";
+source-directory interfaces.d
+
+iface eth0 inet manual
+
+auto eth1
+iface eth1 inet static
+	address  $ip
+	netmask  $nm
+	gateway  $gw
+
+iface eth1 inet6 static
+	address  $ip6
+	netmask  $nm6
+	gateway  $gw6
+
+auto vmbr0
+iface vmbr0 inet static
+	address  10.0.0.2
+	netmask  255.255.255.0
+	gateway  10.0.0.1
+	bridge-ports eth0
+	bridge-stp off
+	bridge-fd 0
+
+CHECK
+expect $final;
+
+save('both', w());
+r(load('both'));
+expect load('both');
+
+# Reload ipv4 and replace instead of modifying
+r(load('ipv4'));
+
+$config->{ifaces}->{eth1} = {
+    type => 'eth',
+    method => 'static',
+    address => $ip,
+    netmask => $nm,
+    gateway => $gw,
+    method6 => 'static',
+    address6 => $ip6,
+    netmask6 => $nm6,
+    gateway6 => $gw6,
+    families => ['inet', 'inet6'],
+    autostart => 1
+};
+expect $final;
+r(w());
+expect $final;
+
+1;
-- 
2.20.1




More information about the pve-devel mailing list