From 55fa4e0976abb01df99c1518f149dc7b86a7ad6e Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sun, 19 Apr 2015 14:07:20 +0200 Subject: [PATCH] implement setup_network for debian --- src/PVE/LXC.pm | 51 ++++++++ src/PVE/LXCSetup.pm | 3 +- src/PVE/LXCSetup/Base.pm | 11 +- src/PVE/LXCSetup/Debian.pm | 152 ++++++++++++++++++++++ src/test/run_tests.pl | 19 ++- src/test/test2/config | 16 +++ src/test/test2/etc/debian_version | 1 + src/test/test2/etc/hostname.exp | 1 + src/test/test2/etc/hosts.exp | 2 + src/test/test2/etc/network/interfaces.exp | 14 ++ 10 files changed, 262 insertions(+), 8 deletions(-) create mode 100644 src/test/test2/config create mode 100644 src/test/test2/etc/debian_version create mode 100644 src/test/test2/etc/hostname.exp create mode 100644 src/test/test2/etc/hosts.exp create mode 100644 src/test/test2/etc/network/interfaces.exp diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm index 0f26c79..c407724 100644 --- a/src/PVE/LXC.pm +++ b/src/PVE/LXC.pm @@ -9,6 +9,7 @@ use Fcntl ':flock'; use PVE::Cluster qw(cfs_register_file cfs_read_file); use PVE::SafeSyslog; use PVE::INotify; +use PVE::Tools qw($IPV6RE $IPV4RE); use Data::Dumper; @@ -613,5 +614,55 @@ sub find_lxc_console_pids { return $res; } +my $ipv4_reverse_mask = [ + '0.0.0.0', + '128.0.0.0', + '192.0.0.0', + '224.0.0.0', + '240.0.0.0', + '248.0.0.0', + '252.0.0.0', + '254.0.0.0', + '255.0.0.0', + '255.128.0.0', + '255.192.0.0', + '255.224.0.0', + '255.240.0.0', + '255.248.0.0', + '255.252.0.0', + '255.254.0.0', + '255.255.0.0', + '255.255.128.0', + '255.255.192.0', + '255.255.224.0', + '255.255.240.0', + '255.255.248.0', + '255.255.252.0', + '255.255.254.0', + '255.255.255.0', + '255.255.255.128', + '255.255.255.192', + '255.255.255.224', + '255.255.255.240', + '255.255.255.248', + '255.255.255.252', + '255.255.255.254', + '255.255.255.255', +]; + +# Note: we cannot use Net:IP, because that only allows strict +# CIDR networks +sub parse_ipv4_cidr { + my ($cidr, $noerr) = @_; + + if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 < 32)) { + return { address => $1, netmask => $ipv4_reverse_mask->[$2] }; + } + + return undef if $noerr; + + die "unable to parse ipv4 address/mask\n"; +} + 1; diff --git a/src/PVE/LXCSetup.pm b/src/PVE/LXCSetup.pm index b9e4e62..d119e3d 100644 --- a/src/PVE/LXCSetup.pm +++ b/src/PVE/LXCSetup.pm @@ -37,8 +37,9 @@ sub new { } sub setup_network { + my ($self) = @_; - + $self->{plugin}->setup_network($self->{conf}); } sub set_hostname { diff --git a/src/PVE/LXCSetup/Base.pm b/src/PVE/LXCSetup/Base.pm index c20c15c..9a49314 100644 --- a/src/PVE/LXCSetup/Base.pm +++ b/src/PVE/LXCSetup/Base.pm @@ -3,6 +3,8 @@ package PVE::LXCSetup::Base; use strict; use warnings; +use PVE::Tools; + sub change_hostname { my ($etc_hosts_data, $hostip, $oldname, $newname) = @_; @@ -102,12 +104,19 @@ sub set_hostname { PVE::Tools::file_set_contents($hosts_fn, $etc_hosts_data); } +sub setup_network { + my ($class, $conf) = @_; + + die "please implement this inside subclass" +} + sub post_create { my ($class, $conf) = @_; + $class->setup_network($conf); $class->set_hostname($conf); - # fixme: what else (network, ...) + # fixme: what else ? } 1; diff --git a/src/PVE/LXCSetup/Debian.pm b/src/PVE/LXCSetup/Debian.pm index e9f72c4..0a37b03 100644 --- a/src/PVE/LXCSetup/Debian.pm +++ b/src/PVE/LXCSetup/Debian.pm @@ -2,11 +2,163 @@ package PVE::LXCSetup::Debian; use strict; use warnings; +use Data::Dumper; use PVE::Tools; +use PVE::LXC; +use File::Path; use PVE::LXCSetup::Base; use base qw(PVE::LXCSetup::Base); +sub setup_network { + my ($class, $conf) = @_; + + my $rootfs = $conf->{'lxc.rootfs'}; + + my $networks = {}; + foreach my $k (keys %$conf) { + next if $k !~ m/^net(\d+)$/; + my $d = $conf->{$k}; + if ($d->{name}) { + my $net = {}; + if (defined($d->{ipv4})) { + my $ipinfo = PVE::LXC::parse_ipv4_cidr($d->{ipv4}); + $net->{v4address} = $ipinfo->{address}; + $net->{v4netmask} = $ipinfo->{netmask}; + } + if (defined($d->{'ipv4.gateway'})) { + $net->{v4gateway} = $d->{'ipv4.gateway'}; + } + if (defined($d->{ipv6})) { + die "implement me"; + } + $networks->{$d->{name}} = $net; + } + } + + return if !scalar(keys %$networks); + + my $filename = "$rootfs/etc/network/interfaces"; + my $data = {}; + my $order = []; + my $interfaces = ""; + + my $section; + + my $done_v4_hash = {}; + my $done_v6_hash = {}; + + my $print_section = sub { + my ($new) = @_; + + return if !$section; + + my $net = $networks->{$section->{ifname}}; + + if ($section->{type} eq 'ipv4') { + $done_v4_hash->{$section->{ifname}} = 1; + + $interfaces .= "auto $section->{ifname}\n" if $new; + + if ($net->{v4address}) { + $interfaces .= "iface $section->{ifname} inet static\n"; + $interfaces .= "\taddress $net->{v4address}\n" if defined($net->{v4address}); + $interfaces .= "\tnetmask $net->{v4netmask}\n" if defined($net->{v4netmask}); + $interfaces .= "\taddress $net->{v4gateway}\n" if defined($net->{v4gateway}); + foreach my $attr (@{$section->{attr}}) { + $interfaces .= "\t$attr\n"; + } + } else { + $interfaces .= "iface $section->{ifname} inet manual\n"; + } + + $interfaces .= "\n"; + + } elsif ($section->{type} eq 'ipv6') { + $done_v6_hash->{$section->{ifname}} = 1; + + if ($net->{v6address}) { + $interfaces .= "iface $section->{ifname} inet6 static\n"; + $interfaces .= "\taddress $net->{v6address}\n" if defined($net->{v6address}); + $interfaces .= "\tnetmask $net->{v6netmask}\n" if defined($net->{v6netmask}); + $interfaces .= "\taddress $net->{v6gateway}\n" if defined($net->{v6gateway}); + foreach my $attr (@{$section->{attr}}) { + $interfaces .= "\t$attr\n"; + } + } + + $interfaces .= "\n"; + } else { + die "unknown section type '$section->{type}'"; + } + + $section = undef; + }; + + if (my $fh = IO::File->new($filename, "r")) { + while (defined (my $line = <$fh>)) { + chomp $line; + if ($line =~ m/^#/) { + $interfaces .= "$line\n"; + next; + } + if ($line =~ m/^\s*$/) { + if ($section) { + &$print_section(); + } else { + $interfaces .= "$line\n"; + } + next; + } + if ($line =~ m/^iface\s+(\S+)\s+inet\s+(\S+)\s*$/) { + my $ifname = $1; + if (!$networks->{$ifname}) { + $interfaces .= "$line\n"; + next; + } + $section = { type => 'ipv4', ifname => $ifname, attr => []}; + next; + } + if ($line =~ m/^iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) { + my $ifname = $1; + if (!$networks->{$ifname}) { + $interfaces .= "$line\n"; + next; + } + $section = { type => 'ipv6', ifname => $ifname, attr => []}; + next; + } + if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) { + my ($adata, $aname) = ($1, $2); + if ($aname eq 'address' || $aname eq 'netmask' || + $aname eq 'gateway' || $aname eq 'broadcast') { + # skip + } else { + push @{$section->{attr}}, $adata; + } + next; + } + + $interfaces .= "$line\n"; + } + &$print_section(); + + } + + foreach my $ifname (sort keys %$networks) { + my $net = $networks->{$ifname}; + if (!$done_v4_hash->{$ifname}) { + $section = { type => 'ipv4', ifname => $ifname, attr => []}; + &$print_section(1); + } + if (!$done_v6_hash->{$ifname} && defined($net->{v6address})) { + $section = { type => 'ipv6', ifname => $ifname, attr => []}; + &$print_section(1); + } + } + + PVE::Tools::file_set_contents($filename, $interfaces); +} 1; diff --git a/src/test/run_tests.pl b/src/test/run_tests.pl index cd07cca..ff14f70 100755 --- a/src/test/run_tests.pl +++ b/src/test/run_tests.pl @@ -12,7 +12,7 @@ use PVE::LXCSetup; sub test_file { my ($exp_fn, $real_fn) = @_; - return if system("diff '$exp_fn' '$real_fn'") == 0; + return if system("diff -u '$exp_fn' '$real_fn'") == 0; die "files does not match\n"; } @@ -35,11 +35,18 @@ sub run_test { my $lxc_setup = PVE::LXCSetup->new($conf); - $lxc_setup->set_hostname(); - - test_file("$testdir/etc/hostname.exp", "$rootfs/etc/hostname"); - test_file("$testdir/etc/hosts.exp", "$rootfs/etc/hosts"); - + for (my $i = 0; $i < 2; $i++) { + # run tests twice, to make sure scripts are idempotent + + $lxc_setup->post_create(); + + my @testfiles = qw(/etc/hostname /etc/hosts /etc/network/interfaces); + foreach my $fn (@testfiles) { + next if !-f "$testdir/$fn.exp"; + test_file("$testdir/$fn.exp", "$rootfs/$fn"); + } + } + print "TEST $testdir => OK\n"; } diff --git a/src/test/test2/config b/src/test/test2/config new file mode 100644 index 0000000..27dacbb --- /dev/null +++ b/src/test/test2/config @@ -0,0 +1,16 @@ +lxc.utsname = test1 +lxc.network.type = veth +lxc.network.link = vmbr0 +lxc.network.name = eth0 +lxc.network.veth.pair = veth100i0 +lxc.network.ipv4 = 1.2.3.4/24 +lxc.network.ipv4.gateway = 1.2.3.1 +lxc.network.type = veth +lxc.network.link = vmbr0 +lxc.network.name = eth1 +lxc.network.veth.pair = veth100i1 +lxc.network.ipv4 = 10.2.3.4/24 +lxc.network.type = veth +lxc.network.link = vmbr0 +lxc.network.name = eth2 +lxc.network.veth.pair = veth100i2 diff --git a/src/test/test2/etc/debian_version b/src/test/test2/etc/debian_version new file mode 100644 index 0000000..4fedf1d --- /dev/null +++ b/src/test/test2/etc/debian_version @@ -0,0 +1 @@ +7.0 diff --git a/src/test/test2/etc/hostname.exp b/src/test/test2/etc/hostname.exp new file mode 100644 index 0000000..a5bce3f --- /dev/null +++ b/src/test/test2/etc/hostname.exp @@ -0,0 +1 @@ +test1 diff --git a/src/test/test2/etc/hosts.exp b/src/test/test2/etc/hosts.exp new file mode 100644 index 0000000..7f3910c --- /dev/null +++ b/src/test/test2/etc/hosts.exp @@ -0,0 +1,2 @@ +127.0.0.1 localhost.localnet localhost +127.0.1.1 test1 diff --git a/src/test/test2/etc/network/interfaces.exp b/src/test/test2/etc/network/interfaces.exp new file mode 100644 index 0000000..f089172 --- /dev/null +++ b/src/test/test2/etc/network/interfaces.exp @@ -0,0 +1,14 @@ +auto eth0 +iface eth0 inet static + address 1.2.3.4 + netmask 255.255.255.0 + address 1.2.3.1 + +auto eth1 +iface eth1 inet static + address 10.2.3.4 + netmask 255.255.255.0 + +auto eth2 +iface eth2 inet manual + -- 2.39.2