use strict;
use warnings;
-use Data::Dumper;
+
use PVE::Tools qw($IPV6RE);
use PVE::LXC;
use PVE::Network;
die "unable to read version info\n" if !defined($version);
- # translate stretch/sid => 9.0 (used on debian testing repository)
- $version = 9.0 if $version eq 'stretch/sid';
+ # translate testing version names
+ $version = 9.1 if $version eq 'stretch/sid';
+ $version = 10 if $version eq 'buster/sid';
+ $version = 11 if $version eq 'bullseye/sid';
die "unable to parse version info '$version'\n"
if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/;
$version = $1;
die "unsupported debian version '$version'\n"
- if !($version >= 4 && $version <= 9);
+ if !($version >= 4 && $version <= 11);
my $self = { conf => $conf, rootdir => $rootdir, version => $version };
return bless $self, $class;
}
+# Debian doesn't support the /dev/lxc/ subdirectory.
+sub devttydir {
+ return '';
+}
+
sub setup_init {
my ($self, $conf) = @_;
my $systemd = $self->ct_readlink('/sbin/init');
if (defined($systemd) && $systemd =~ m@/systemd$@) {
- $self->setup_container_getty_service(1);
+ $self->setup_container_getty_service($conf);
}
my $filename = "/etc/inittab";
my @lines = grep {
# remove getty lines
- !/^\s*\d+:\d+:[^:]*:.*getty/ &&
+ !/^\s*\d+:\d*:[^:]*:.*getty/ &&
# remove power lines
!/^\s*p[fno0]:/
} split(/\n/, $inittab);
sub remove_gateway_scripts {
my ($attr) = @_;
my $length = scalar(@$attr);
- for (my $i = 0; $i < $length; ++$i) {
- my $a = $attr->[$i];
- if ($a =~ m@^\s*post-up\s+.*route.*add.*default.*(?:gw|via)\s+(\S+)@) {
- my $gw = $1;
- if ($i > 0 && $attr->[$i-1] =~ m@^\s*post-up\s+.*route.*add.*\Q$1\E@) {
- --$i;
- splice @$attr, $i, 2;
- $length -= 2;
- } else {
- splice @$attr, $i, 1;
- $length -= 1;
- }
- --$i;
- next;
+
+ my $found_section = 0;
+ my $keep = 1;
+ @$attr = grep {
+ if ($_ eq '# --- BEGIN PVE ---') {
+ $found_section = 1;
+ $keep = 0;
+ 0; # remove this line
+ } elsif ($_ eq '# --- END PVE ---') {
+ $found_section = 1;
+ $keep = 1;
+ 0; # remove this line
+ } else {
+ $keep;
}
- if ($a =~ m@^\s*pre-down\s+.*route.*del.*default.*(?:gw|via)\s+(\S+)@) {
- my $gw = $1;
- if ($attr->[$i+1] =~ m@^\s*pre-down\s+.*route.*del.*\Q$1\E@) {
- splice @$attr, $i, 2;
- $length -= 2;
- } else {
- splice @$attr, $i, 1;
- $length -= 1;
- }
+ } @$attr;
+
+ return if $found_section;
+ # XXX: To deal with existing setups we perform two types of removal for
+ # now. Newly started containers have their routing sections marked with
+ # begin/end comments. For older containers we perform a strict matching on
+ # the routing rules we added. We can probably remove this part at some point
+ # when it is unlikely that old debian setups are still around.
+
+ for (my $i = 0; $i < $length-3; ++$i) {
+ next if $attr->[$i+0] !~ m@^\s*post-up\s+ip\s+route\s+add\s+(\S+)\s+dev\s+(\S+)$@;
+ my ($ip, $dev) = ($1, $2);
+ if ($attr->[$i+1] =~ m@^\s*post-up\s+ip\s+route\s+add\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2) &&
+ $attr->[$i+2] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2) &&
+ $attr->[$i+3] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2))
+ {
+ splice @$attr, $i, 4;
+ $length -= 4;
--$i;
- next;
}
}
}
sub make_gateway_scripts {
my ($ifname, $gw) = @_;
return <<"SCRIPTS";
+# --- BEGIN PVE ---
\tpost-up ip route add $gw dev $ifname
-\tpost-up ip route add default via $gw
-\tpre-down ip route del default via $gw
+\tpost-up ip route add default via $gw dev $ifname
+\tpre-down ip route del default via $gw dev $ifname
\tpre-down ip route del $gw dev $ifname
+# --- END PVE ---
SCRIPTS
}
if ($section->{type} eq 'ipv4') {
$done_v4_hash->{$ifname} = 1;
- if ($net->{address} =~ /^(dhcp|manual)$/) {
- $interfaces .= "iface $ifname inet $1\n";
+ if (!defined($net->{address})) {
+ # no address => no iface line
+ } elsif ($net->{address} =~ /^(dhcp|manual)$/) {
+ $interfaces .= "iface $ifname inet $1\n\n";
} else {
$interfaces .= "iface $ifname inet static\n";
$interfaces .= "\taddress $net->{address}\n" if defined($net->{address});
$interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask});
+
+ remove_gateway_scripts($section->{attr});
if (defined(my $gw = $net->{gateway})) {
- remove_gateway_scripts($section->{attr});
if ($net->{needsroute}) {
$interfaces .= make_gateway_scripts($ifname, $gw);
} else {
foreach my $attr (@{$section->{attr}}) {
$interfaces .= "\t$attr\n";
}
+ $interfaces .= "\n";
}
-
- $interfaces .= "\n";
-
} elsif ($section->{type} eq 'ipv6') {
$done_v6_hash->{$ifname} = 1;
- if ($net->{address6} =~ /^(auto|dhcp|manual)$/) {
- $interfaces .= "iface $ifname inet6 $1\n";
+ if (!defined($net->{address6})) {
+ # no address => no iface line
+ } elsif ($net->{address6} =~ /^(auto|dhcp|manual)$/) {
+ $interfaces .= "iface $ifname inet6 $1\n\n";
} else {
$interfaces .= "iface $ifname inet6 static\n";
$interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6});
$interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6});
+ remove_gateway_scripts($section->{attr});
if (defined(my $gw = $net->{gateway6})) {
- remove_gateway_scripts($section->{attr});
if ($net->{needsroute6}) {
$interfaces .= make_gateway_scripts($ifname, $gw);
} else {
foreach my $attr (@{$section->{attr}}) {
$interfaces .= "\t$attr\n";
}
+ $interfaces .= "\n";
}
-
- $interfaces .= "\n";
} else {
die "unknown section type '$section->{type}'";
}
if (my $fh = $self->ct_open_file_read($filename)) {
while (defined (my $line = <$fh>)) {
chomp $line;
+ if ($line =~ m/^# --- (?:BEGIN|END) PVE ---/) {
+ # Include markers in the attribute section so
+ # remove_gateway_scripts() can find them.
+ push @{$section->{attr}}, $line if $section;
+ next;
+ }
if ($line =~ m/^#/) {
$interfaces .= "$line\n";
next;
}
}
+ # older templates (< Debian 8) do not configure the loopback interface
+ # if not explicitly told to do so
+ if (!$done_auto->{lo}) {
+ $interfaces = "auto lo\niface lo inet loopback\n" .
+ "iface lo inet6 loopback\n\n" .
+ $interfaces;
+ }
+
$self->ct_file_set_contents($filename, $interfaces);
}