]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXCSetup/Debian.pm
debian setup: dhcp, manual and unmanaged network
[pve-container.git] / src / PVE / LXCSetup / Debian.pm
CommitLineData
1c7f4f65
DM
1package PVE::LXCSetup::Debian;
2
3use strict;
4use warnings;
55fa4e09 5use Data::Dumper;
a1b1a247 6use PVE::Tools qw($IPV6RE);
55fa4e09
DM
7use PVE::LXC;
8use File::Path;
1c7f4f65
DM
9
10use PVE::LXCSetup::Base;
11
12use base qw(PVE::LXCSetup::Base);
13
633a7bd8 14sub new {
5b4657d0 15 my ($class, $conf, $rootdir) = @_;
633a7bd8 16
5b4657d0 17 my $version = PVE::Tools::file_read_firstline("$rootdir/etc/debian_version");
633a7bd8
DM
18
19 die "unable to read version info\n" if !defined($version);
adc8f577
DM
20
21 die "unable to parse version info\n"
22 if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/;
23
24 $version = $1;
25
633a7bd8
DM
26 die "unsupported debian version '$version'\n" if $version < 6;
27
5b4657d0
DM
28 my $self = { conf => $conf, rootdir => $rootdir, version => $version };
29
30 $conf->{'lxc.include'} = "/usr/share/lxc/config/debian.common.conf";
633a7bd8
DM
31
32 return bless $self, $class;
33}
34
d66768a2
DM
35my $default_inittab = <<__EOD__;
36
37# The default runlevel.
38id:2:initdefault:
39
40# Boot-time system configuration/initialization script.
41# This is run first except when booting in emergency (-b) mode.
42si::sysinit:/etc/init.d/rcS
43
44# /etc/init.d executes the S and K scripts upon change
45# of runlevel.
46#
47# Runlevel 0 is halt.
48# Runlevel 1 is single-user.
49# Runlevels 2-5 are multi-user.
50# Runlevel 6 is reboot.
51
52l0:0:wait:/etc/init.d/rc 0
53l1:1:wait:/etc/init.d/rc 1
54l2:2:wait:/etc/init.d/rc 2
55l3:3:wait:/etc/init.d/rc 3
56l4:4:wait:/etc/init.d/rc 4
57l5:5:wait:/etc/init.d/rc 5
58l6:6:wait:/etc/init.d/rc 6
59# Normally not reached, but fallthrough in case of emergency.
60z6:6:respawn:/sbin/sulogin
61
62# What to do when CTRL-ALT-DEL is pressed.
63ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
64
65# What to do when the power fails/returns.
66p0::powerfail:/sbin/init 0
67
68# /sbin/getty invocations for the runlevels.
69#
70# The "id" field MUST be the same as the last
71# characters of the device (after "tty").
72#
73# Format:
74# <id>:<runlevels>:<action>:<process>
75#
76__EOD__
77
78sub setup_init {
633a7bd8 79 my ($self, $conf) = @_;
d66768a2 80
5b4657d0 81 my $rootdir = $self->{rootdir};
d66768a2 82
5b4657d0 83 my $filename = "$rootdir/etc/inittab";
d66768a2
DM
84
85 if (-f $filename) {
86 my $inittab = $default_inittab;
87
88 my $ttycount = defined($conf->{'lxc.tty'}) ? $conf->{'lxc.tty'} : 4;
89 for (my $i = 1; $i <= $ttycount; $i++) {
90 next if $i == 7; # reserved for X11
91 my $levels = ($i == 1) ? '2345' : '23';
5202218d
DM
92 if ($self->{version} < 7) {
93 $inittab .= "$i:$levels:respawn:/sbin/getty -L 38400 tty$i\n";
94 } else {
95 $inittab .= "$i:$levels:respawn:/sbin/getty --noclear 38400 tty$i\n";
96 }
d66768a2
DM
97 }
98
99 PVE::Tools::file_set_contents($filename, $inittab);
100 }
101}
102
55fa4e09 103sub setup_network {
633a7bd8 104 my ($self, $conf) = @_;
55fa4e09 105
5b4657d0 106 my $rootdir = $self->{rootdir};
55fa4e09
DM
107
108 my $networks = {};
109 foreach my $k (keys %$conf) {
110 next if $k !~ m/^net(\d+)$/;
93285df8 111 my $ind = $1;
55fa4e09
DM
112 my $d = $conf->{$k};
113 if ($d->{name}) {
114 my $net = {};
93285df8 115 if (defined($d->{ip})) {
0178cffa
WB
116 if ($d->{ip} =~ /^(?:dhcp|manual)$/) {
117 $net->{address} = $d->{ip};
118 } else {
119 my $ipinfo = PVE::LXC::parse_ipv4_cidr($d->{ip});
120 $net->{address} = $ipinfo->{address};
121 $net->{netmask} = $ipinfo->{netmask};
122 }
55fa4e09 123 }
93285df8 124 if (defined($d->{'gw'})) {
a1b1a247 125 $net->{gateway} = $d->{'gw'};
55fa4e09 126 }
93285df8 127 if (defined($d->{ip6})) {
0178cffa
WB
128 if ($d->{ip6} =~ /^(?:dhcp|manual)$/) {
129 $net->{address6} = $d->{ip6};
130 } elsif ($d->{ip6} !~ /^($IPV6RE)\/(\d+)$/) {
a1b1a247 131 die "unable to parse ipv6 address/prefix\n";
0178cffa
WB
132 } else {
133 $net->{address6} = $1;
134 $net->{netmask6} = $2;
a1b1a247 135 }
a1b1a247
WB
136 }
137 if (defined($d->{'gw6'})) {
138 $net->{gateway6} = $d->{'gw6'};
55fa4e09 139 }
0178cffa 140 $networks->{$d->{name}} = $net if keys %$net;
55fa4e09
DM
141 }
142 }
143
f44d23a5 144 return if !scalar(keys %$networks);
55fa4e09 145
5b4657d0 146 my $filename = "$rootdir/etc/network/interfaces";
55fa4e09
DM
147 my $interfaces = "";
148
149 my $section;
150
151 my $done_v4_hash = {};
152 my $done_v6_hash = {};
153
154 my $print_section = sub {
155 my ($new) = @_;
156
157 return if !$section;
158
159 my $net = $networks->{$section->{ifname}};
160
161 if ($section->{type} eq 'ipv4') {
162 $done_v4_hash->{$section->{ifname}} = 1;
163
164 $interfaces .= "auto $section->{ifname}\n" if $new;
165
0178cffa
WB
166 if ($net->{address} =~ /^(dhcp|manual)$/) {
167 $interfaces .= "iface $section->{ifname} inet $1\n";
168 } elsif ($net->{address}) {
55fa4e09 169 $interfaces .= "iface $section->{ifname} inet static\n";
a1b1a247
WB
170 $interfaces .= "\taddress $net->{address}\n" if defined($net->{address});
171 $interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask});
172 $interfaces .= "\tgateway $net->{gateway}\n" if defined($net->{gateway});
55fa4e09
DM
173 foreach my $attr (@{$section->{attr}}) {
174 $interfaces .= "\t$attr\n";
175 }
55fa4e09
DM
176 }
177
178 $interfaces .= "\n";
179
180 } elsif ($section->{type} eq 'ipv6') {
181 $done_v6_hash->{$section->{ifname}} = 1;
182
0178cffa
WB
183 if ($net->{address6} =~ /^(dhcp|manual)$/) {
184 $interfaces .= "iface $section->{ifname} inet6 $1\n";
185 } elsif ($net->{address6}) {
55fa4e09 186 $interfaces .= "iface $section->{ifname} inet6 static\n";
a1b1a247
WB
187 $interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6});
188 $interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6});
189 $interfaces .= "\tgateway $net->{gateway6}\n" if defined($net->{gateway6});
55fa4e09
DM
190 foreach my $attr (@{$section->{attr}}) {
191 $interfaces .= "\t$attr\n";
192 }
193 }
194
195 $interfaces .= "\n";
196 } else {
197 die "unknown section type '$section->{type}'";
198 }
199
200 $section = undef;
201 };
202
203 if (my $fh = IO::File->new($filename, "r")) {
204 while (defined (my $line = <$fh>)) {
205 chomp $line;
206 if ($line =~ m/^#/) {
207 $interfaces .= "$line\n";
208 next;
209 }
210 if ($line =~ m/^\s*$/) {
211 if ($section) {
212 &$print_section();
213 } else {
214 $interfaces .= "$line\n";
215 }
216 next;
217 }
0178cffa 218 if ($line =~ m/^\s*iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
55fa4e09 219 my $ifname = $1;
0178cffa 220 &$print_section(); # print previous section
55fa4e09
DM
221 if (!$networks->{$ifname}) {
222 $interfaces .= "$line\n";
223 next;
224 }
225 $section = { type => 'ipv4', ifname => $ifname, attr => []};
226 next;
227 }
0178cffa 228 if ($line =~ m/^\s*iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) {
55fa4e09 229 my $ifname = $1;
0178cffa 230 &$print_section(); # print previous section
55fa4e09
DM
231 if (!$networks->{$ifname}) {
232 $interfaces .= "$line\n";
233 next;
234 }
235 $section = { type => 'ipv6', ifname => $ifname, attr => []};
236 next;
237 }
0178cffa
WB
238 # Handle other section delimiters:
239 if ($line =~ m/^\s*(?:mapping\s
240 |auto\s
241 |allow-
242 |source\s
243 |source-directory\s
244 )/x) {
245 &$print_section();
246 $interfaces .= "$line\n";
247 next;
248 }
55fa4e09
DM
249 if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) {
250 my ($adata, $aname) = ($1, $2);
251 if ($aname eq 'address' || $aname eq 'netmask' ||
252 $aname eq 'gateway' || $aname eq 'broadcast') {
253 # skip
254 } else {
255 push @{$section->{attr}}, $adata;
256 }
257 next;
258 }
259
260 $interfaces .= "$line\n";
261 }
262 &$print_section();
263
264 }
265
93285df8 266 my $need_separator = 1;
55fa4e09
DM
267 foreach my $ifname (sort keys %$networks) {
268 my $net = $networks->{$ifname};
93285df8 269
55fa4e09 270 if (!$done_v4_hash->{$ifname}) {
93285df8 271 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09
DM
272 $section = { type => 'ipv4', ifname => $ifname, attr => []};
273 &$print_section(1);
274 }
a1b1a247 275 if (!$done_v6_hash->{$ifname} && defined($net->{address6})) {
93285df8 276 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09
DM
277 $section = { type => 'ipv6', ifname => $ifname, attr => []};
278 &$print_section(1);
279 }
280 }
281
282 PVE::Tools::file_set_contents($filename, $interfaces);
283}
1c7f4f65
DM
284
2851;