]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXCSetup/Debian.pm
LXC::get_primary_ips: fix ip/ip6 mixup
[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
DM
115 if (defined($d->{ip})) {
116 my $ipinfo = PVE::LXC::parse_ipv4_cidr($d->{ip});
a1b1a247
WB
117 $net->{address} = $ipinfo->{address};
118 $net->{netmask} = $ipinfo->{netmask};
55fa4e09 119 }
93285df8 120 if (defined($d->{'gw'})) {
a1b1a247 121 $net->{gateway} = $d->{'gw'};
55fa4e09 122 }
93285df8 123 if (defined($d->{ip6})) {
a1b1a247
WB
124 if ($d->{ip6} !~ /^($IPV6RE)\/(\d+)$/) {
125 die "unable to parse ipv6 address/prefix\n";
126 }
127 $net->{address6} = $1;
128 $net->{netmask6} = $2;
129 }
130 if (defined($d->{'gw6'})) {
131 $net->{gateway6} = $d->{'gw6'};
55fa4e09
DM
132 }
133 $networks->{$d->{name}} = $net;
134 }
135 }
136
93285df8 137 return if !scalar(keys %$networks);
55fa4e09 138
5b4657d0 139 my $filename = "$rootdir/etc/network/interfaces";
55fa4e09
DM
140 my $data = {};
141 my $order = [];
142 my $interfaces = "";
143
144 my $section;
145
146 my $done_v4_hash = {};
147 my $done_v6_hash = {};
148
149 my $print_section = sub {
150 my ($new) = @_;
151
152 return if !$section;
153
154 my $net = $networks->{$section->{ifname}};
155
156 if ($section->{type} eq 'ipv4') {
157 $done_v4_hash->{$section->{ifname}} = 1;
158
159 $interfaces .= "auto $section->{ifname}\n" if $new;
160
a1b1a247 161 if ($net->{address}) {
55fa4e09 162 $interfaces .= "iface $section->{ifname} inet static\n";
a1b1a247
WB
163 $interfaces .= "\taddress $net->{address}\n" if defined($net->{address});
164 $interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask});
165 $interfaces .= "\tgateway $net->{gateway}\n" if defined($net->{gateway});
55fa4e09
DM
166 foreach my $attr (@{$section->{attr}}) {
167 $interfaces .= "\t$attr\n";
168 }
169 } else {
170 $interfaces .= "iface $section->{ifname} inet manual\n";
171 }
172
173 $interfaces .= "\n";
174
175 } elsif ($section->{type} eq 'ipv6') {
176 $done_v6_hash->{$section->{ifname}} = 1;
177
a1b1a247 178 if ($net->{address6}) {
55fa4e09 179 $interfaces .= "iface $section->{ifname} inet6 static\n";
a1b1a247
WB
180 $interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6});
181 $interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6});
182 $interfaces .= "\tgateway $net->{gateway6}\n" if defined($net->{gateway6});
55fa4e09
DM
183 foreach my $attr (@{$section->{attr}}) {
184 $interfaces .= "\t$attr\n";
185 }
186 }
187
188 $interfaces .= "\n";
189 } else {
190 die "unknown section type '$section->{type}'";
191 }
192
193 $section = undef;
194 };
195
196 if (my $fh = IO::File->new($filename, "r")) {
197 while (defined (my $line = <$fh>)) {
198 chomp $line;
199 if ($line =~ m/^#/) {
200 $interfaces .= "$line\n";
201 next;
202 }
203 if ($line =~ m/^\s*$/) {
204 if ($section) {
205 &$print_section();
206 } else {
207 $interfaces .= "$line\n";
208 }
209 next;
210 }
211 if ($line =~ m/^iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
212 my $ifname = $1;
213 if (!$networks->{$ifname}) {
214 $interfaces .= "$line\n";
215 next;
216 }
217 $section = { type => 'ipv4', ifname => $ifname, attr => []};
218 next;
219 }
220 if ($line =~ m/^iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) {
221 my $ifname = $1;
222 if (!$networks->{$ifname}) {
223 $interfaces .= "$line\n";
224 next;
225 }
226 $section = { type => 'ipv6', ifname => $ifname, attr => []};
227 next;
228 }
229 if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) {
230 my ($adata, $aname) = ($1, $2);
231 if ($aname eq 'address' || $aname eq 'netmask' ||
232 $aname eq 'gateway' || $aname eq 'broadcast') {
233 # skip
234 } else {
235 push @{$section->{attr}}, $adata;
236 }
237 next;
238 }
239
240 $interfaces .= "$line\n";
241 }
242 &$print_section();
243
244 }
245
93285df8 246 my $need_separator = 1;
55fa4e09
DM
247 foreach my $ifname (sort keys %$networks) {
248 my $net = $networks->{$ifname};
93285df8 249
55fa4e09 250 if (!$done_v4_hash->{$ifname}) {
93285df8 251 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09
DM
252 $section = { type => 'ipv4', ifname => $ifname, attr => []};
253 &$print_section(1);
254 }
a1b1a247 255 if (!$done_v6_hash->{$ifname} && defined($net->{address6})) {
93285df8 256 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09
DM
257 $section = { type => 'ipv6', ifname => $ifname, attr => []};
258 &$print_section(1);
259 }
260 }
261
262 PVE::Tools::file_set_contents($filename, $interfaces);
263}
1c7f4f65
DM
264
2651;