[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