]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXC.pm
bump version to 0.9-2
[pve-container.git] / src / PVE / LXC.pm
CommitLineData
f76a2828
DM
1package PVE::LXC;
2
3use strict;
4use warnings;
d14a9a1b 5use POSIX qw(EINTR);
f76a2828
DM
6
7use File::Path;
8use Fcntl ':flock';
9
10use PVE::Cluster qw(cfs_register_file cfs_read_file);
c65e0a6d 11use PVE::Storage;
f76a2828
DM
12use PVE::SafeSyslog;
13use PVE::INotify;
a3249355 14use PVE::JSONSchema qw(get_standard_option);
55fa4e09 15use PVE::Tools qw($IPV6RE $IPV4RE);
68fba17b 16use PVE::Network;
f76a2828
DM
17
18use Data::Dumper;
19
20cfs_register_file('/lxc/', \&parse_lxc_config, \&write_lxc_config);
21
7dfc49cc
DM
22PVE::JSONSchema::register_format('pve-lxc-network', \&verify_lxc_network);
23sub verify_lxc_network {
24 my ($value, $noerr) = @_;
25
26 return $value if parse_lxc_network($value);
27
28 return undef if $noerr;
29
30 die "unable to parse network setting\n";
31}
32
f76a2828
DM
33my $nodename = PVE::INotify::nodename();
34
822de0c3
DM
35sub parse_lxc_size {
36 my ($name, $value) = @_;
37
38 if ($value =~ m/^(\d+)(b|k|m|g)?$/i) {
39 my ($res, $unit) = ($1, lc($2 || 'b'));
40
41 return $res if $unit eq 'b';
42 return $res*1024 if $unit eq 'k';
43 return $res*1024*1024 if $unit eq 'm';
44 return $res*1024*1024*1024 if $unit eq 'g';
45 }
46
47 return undef;
48}
49
f76a2828 50my $valid_lxc_keys = {
822de0c3 51 'lxc.arch' => 'i386|x86|i686|x86_64|amd64',
f76a2828
DM
52 'lxc.include' => 1,
53 'lxc.rootfs' => 1,
54 'lxc.mount' => 1,
55 'lxc.utsname' => 1,
56
54664cd3 57 'lxc.id_map' => 1,
cbb03fea 58
822de0c3 59 'lxc.cgroup.memory.limit_in_bytes' => \&parse_lxc_size,
44da0641 60 'lxc.cgroup.memory.memsw.limit_in_bytes' => \&parse_lxc_size,
b80dd50a
DM
61 'lxc.cgroup.cpu.cfs_period_us' => '\d+',
62 'lxc.cgroup.cpu.cfs_quota_us' => '\d+',
63 'lxc.cgroup.cpu.shares' => '\d+',
cbb03fea 64
54664cd3
DM
65 # mount related
66 'lxc.mount' => 1,
67 'lxc.mount.entry' => 1,
68 'lxc.mount.auto' => 1,
69
70 # not used by pve
b80dd50a 71 'lxc.tty' => '\d+',
54664cd3
DM
72 'lxc.pts' => 1,
73 'lxc.haltsignal' => 1,
cbb03fea 74 'lxc.rebootsignal' => 1,
54664cd3
DM
75 'lxc.stopsignal' => 1,
76 'lxc.init_cmd' => 1,
77 'lxc.console' => 1,
78 'lxc.console.logfile' => 1,
79 'lxc.devttydir' => 1,
80 'lxc.autodev' => 1,
81 'lxc.kmsg' => 1,
82 'lxc.cap.drop' => 1,
83 'lxc.cap.keep' => 1,
84 'lxc.aa_profile' => 1,
85 'lxc.aa_allow_incomplete' => 1,
86 'lxc.se_context' => 1,
87 'lxc.loglevel' => 1,
88 'lxc.logfile' => 1,
89 'lxc.environment' => 1,
0d3abdf7 90 'lxc.cgroup.devices.deny' => 1,
54664cd3
DM
91
92 # autostart
93 'lxc.start.auto' => 1,
94 'lxc.start.delay' => 1,
95 'lxc.start.order' => 1,
96 'lxc.group' => 1,
97
98 # hooks
99 'lxc.hook.pre-start' => 1,
100 'lxc.hook.pre-mount' => 1,
101 'lxc.hook.mount' => 1,
102 'lxc.hook.autodev' => 1,
103 'lxc.hook.start' => 1,
104 'lxc.hook.post-stop' => 1,
105 'lxc.hook.clone' => 1,
cbb03fea 106
f76a2828 107 # pve related keys
117636e5
DM
108 'pve.nameserver' => sub {
109 my ($name, $value) = @_;
110 return verify_nameserver_list($value);
111 },
112 'pve.searchdomain' => sub {
113 my ($name, $value) = @_;
114 return verify_searchdomain_list($value);
115 },
a99b3509 116 'pve.onboot' => '(0|1)',
a3249355
DM
117 'pve.startup' => sub {
118 my ($name, $value) = @_;
119 return PVE::JSONSchema::pve_verify_startup_order($value);
120 },
f76a2828 121 'pve.comment' => 1,
10fc3ba5 122 'pve.disksize' => '\d+(\.\d+)?',
611fe3aa
DM
123 'pve.volid' => sub {
124 my ($name, $value) = @_;
125 PVE::Storage::parse_volume_id($value);
126 return $value;
127 },
f76a2828
DM
128};
129
93285df8 130my $valid_lxc_network_keys = {
f76a2828 131 type => 1,
f76a2828
DM
132 mtu => 1,
133 name => 1, # ifname inside container
134 'veth.pair' => 1, # ifname at host (eth${vmid}.X)
135 hwaddr => 1,
93285df8
DM
136};
137
138my $valid_pve_network_keys = {
8a04b6c7 139 bridge => 1,
2b1fc2ea
DM
140 tag => 1,
141 firewall => 1,
93285df8
DM
142 ip => 1,
143 gw => 1,
144 ip6 => 1,
145 gw6 => 1,
f76a2828
DM
146};
147
148my $lxc_array_configs = {
149 'lxc.network' => 1,
150 'lxc.mount' => 1,
151 'lxc.include' => 1,
5b4657d0 152 'lxc.id_map' => 1,
0d3abdf7 153 'lxc.cgroup.devices.deny' => 1,
f76a2828
DM
154};
155
156sub write_lxc_config {
157 my ($filename, $data) = @_;
158
159 my $raw = "";
160
161 return $raw if !$data;
162
163 my $done_hash = { digest => 1};
7dfc49cc 164
63e9c5ca
DM
165 my $dump_entry = sub {
166 my ($k) = @_;
167 my $value = $data->{$k};
168 return if !defined($value);
169 return if $done_hash->{$k};
f76a2828 170 $done_hash->{$k} = 1;
63e9c5ca 171 if (ref($value)) {
cbb03fea 172 die "got unexpected reference for '$k'"
63e9c5ca
DM
173 if !$lxc_array_configs->{$k};
174 foreach my $v (@$value) {
5b4657d0
DM
175 $raw .= "$k = $v\n";
176 }
177 } else {
63e9c5ca 178 $raw .= "$k = $value\n";
5b4657d0 179 }
63e9c5ca
DM
180 };
181
182 # Note: Order is important! Include defaults first, so that we
183 # can overwrite them later.
184 &$dump_entry('lxc.include');
cbb03fea 185
63e9c5ca
DM
186 foreach my $k (sort keys %$data) {
187 next if $k !~ m/^lxc\./;
188 &$dump_entry($k);
f76a2828 189 }
7dfc49cc 190
f76a2828 191 foreach my $k (sort keys %$data) {
fff3a342 192 next if $k !~ m/^pve\./;
63e9c5ca 193 &$dump_entry($k);
fff3a342
DM
194 }
195
160f0941 196 my $network_count = 0;
fff3a342 197 foreach my $k (sort keys %$data) {
f76a2828
DM
198 next if $k !~ m/^net\d+$/;
199 $done_hash->{$k} = 1;
200 my $net = $data->{$k};
160f0941 201 $network_count++;
f76a2828
DM
202 $raw .= "lxc.network.type = $net->{type}\n";
203 foreach my $subkey (sort keys %$net) {
204 next if $subkey eq 'type';
93285df8
DM
205 if ($valid_lxc_network_keys->{$subkey}) {
206 $raw .= "lxc.network.$subkey = $net->{$subkey}\n";
207 } elsif ($valid_pve_network_keys->{$subkey}) {
208 $raw .= "pve.network.$subkey = $net->{$subkey}\n";
209 } else {
210 die "found invalid network key '$subkey'";
211 }
f76a2828
DM
212 }
213 }
214
160f0941
DM
215 if (!$network_count) {
216 $raw .= "lxc.network.type = empty\n";
217 }
218
f76a2828
DM
219 foreach my $k (sort keys %$data) {
220 next if $done_hash->{$k};
221 die "found un-written value in config - implement this!";
222 }
223
f76a2828
DM
224 return $raw;
225}
226
822de0c3
DM
227sub parse_lxc_option {
228 my ($name, $value) = @_;
229
230 my $parser = $valid_lxc_keys->{$name};
231
fe81a350 232 die "invalid key '$name'\n" if !defined($parser);
822de0c3
DM
233
234 if ($parser eq '1') {
cbb03fea 235 return $value;
822de0c3
DM
236 } elsif (ref($parser)) {
237 my $res = &$parser($name, $value);
238 return $res if defined($res);
239 } else {
240 # assume regex
241 return $value if $value =~ m/^$parser$/;
242 }
cbb03fea 243
822de0c3
DM
244 die "unable to parse value '$value' for option '$name'\n";
245}
246
f76a2828
DM
247sub parse_lxc_config {
248 my ($filename, $raw) = @_;
249
250 return undef if !defined($raw);
251
252 my $data = {
253 digest => Digest::SHA::sha1_hex($raw),
254 };
255
256 $filename =~ m|/lxc/(\d+)/config$|
257 || die "got strange filename '$filename'";
258
259 my $vmid = $1;
260
261 my $network_counter = 0;
262 my $network_list = [];
263 my $host_ifnames = {};
264
265 my $find_next_hostif_name = sub {
266 for (my $i = 0; $i < 10; $i++) {
267 my $name = "veth${vmid}.$i";
268 if (!$host_ifnames->{$name}) {
269 $host_ifnames->{$name} = 1;
270 return $name;
271 }
272 }
273
274 die "unable to find free host_ifname"; # should not happen
275 };
7dfc49cc 276
f76a2828
DM
277 my $push_network = sub {
278 my ($netconf) = @_;
279 return if !$netconf;
280 push @{$network_list}, $netconf;
281 $network_counter++;
282 if (my $netname = $netconf->{'veth.pair'}) {
283 if ($netname =~ m/^veth(\d+).(\d)$/) {
284 die "wrong vmid for network interface pair\n" if $1 != $vmid;
285 my $host_ifnames->{$netname} = 1;
286 } else {
287 die "wrong network interface pair\n";
288 }
289 }
290 };
291
292 my $network;
7dfc49cc 293
f76a2828
DM
294 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
295 my $line = $1;
296
297 next if $line =~ m/^\#/;
298 next if $line =~ m/^\s*$/;
299
300 if ($line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
301 my ($subkey, $value) = ($1, $2);
302 if ($subkey eq 'type') {
303 &$push_network($network);
304 $network = { type => $value };
93285df8
DM
305 } elsif ($valid_lxc_network_keys->{$subkey}) {
306 $network->{$subkey} = $value;
307 } else {
308 die "unable to parse config line: $line\n";
309 }
310 next;
311 }
312 if ($line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
313 my ($subkey, $value) = ($1, $2);
314 if ($valid_pve_network_keys->{$subkey}) {
f76a2828
DM
315 $network->{$subkey} = $value;
316 } else {
317 die "unable to parse config line: $line\n";
318 }
f76a2828
DM
319 next;
320 }
321 if ($line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/) {
322 my ($name, $value) = ($1, $2);
323 $data->{$name} = $value;
324 next;
325 }
54664cd3 326 if ($line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/) {
f76a2828
DM
327 my ($name, $value) = ($1, $2);
328
5b4657d0
DM
329 if ($lxc_array_configs->{$name}) {
330 $data->{$name} = [] if !defined($data->{$name});
331 push @{$data->{$name}}, parse_lxc_option($name, $value);
332 } else {
333 die "multiple definitions for $name\n" if defined($data->{$name});
334 $data->{$name} = parse_lxc_option($name, $value);
335 }
cbb03fea 336
f76a2828
DM
337 next;
338 }
339
340 die "unable to parse config line: $line\n";
341 }
342
343 &$push_network($network);
344
345 foreach my $net (@{$network_list}) {
160f0941 346 next if $net->{type} eq 'empty'; # skip
f76a2828
DM
347 $net->{'veth.pair'} = &$find_next_hostif_name() if !$net->{'veth.pair'};
348 $net->{hwaddr} = PVE::Tools::random_ether_addr() if !$net->{hwaddr};
349 die "unsupported network type '$net->{type}'\n" if $net->{type} ne 'veth';
350
351 if ($net->{'veth.pair'} =~ m/^veth\d+.(\d+)$/) {
352 $data->{"net$1"} = $net;
353 }
7dfc49cc
DM
354 }
355
356 return $data;
f76a2828
DM
357}
358
359sub config_list {
360 my $vmlist = PVE::Cluster::get_vmlist();
361 my $res = {};
362 return $res if !$vmlist || !$vmlist->{ids};
363 my $ids = $vmlist->{ids};
364
365 foreach my $vmid (keys %$ids) {
366 next if !$vmid; # skip CT0
367 my $d = $ids->{$vmid};
368 next if !$d->{node} || $d->{node} ne $nodename;
369 next if !$d->{type} || $d->{type} ne 'lxc';
370 $res->{$vmid}->{type} = 'lxc';
371 }
372 return $res;
373}
374
375sub cfs_config_path {
376 my ($vmid, $node) = @_;
377
378 $node = $nodename if !$node;
379 return "nodes/$node/lxc/$vmid/config";
380}
381
9c2d4ce9
DM
382sub config_file {
383 my ($vmid, $node) = @_;
384
385 my $cfspath = cfs_config_path($vmid, $node);
386 return "/etc/pve/$cfspath";
387}
388
f76a2828
DM
389sub load_config {
390 my ($vmid) = @_;
391
392 my $cfspath = cfs_config_path($vmid);
393
394 my $conf = PVE::Cluster::cfs_read_file($cfspath);
395 die "container $vmid does not exists\n" if !defined($conf);
396
397 return $conf;
398}
399
5b4657d0
DM
400sub create_config {
401 my ($vmid, $conf) = @_;
402
403 my $dir = "/etc/pve/nodes/$nodename/lxc";
404 mkdir $dir;
405
406 $dir .= "/$vmid";
407 mkdir($dir) || die "unable to create container configuration directory - $!\n";
408
409 write_config($vmid, $conf);
410}
411
412sub destroy_config {
413 my ($vmid) = @_;
414
415 my $dir = "/etc/pve/nodes/$nodename/lxc/$vmid";
416 File::Path::rmtree($dir);
417}
418
f76a2828
DM
419sub write_config {
420 my ($vmid, $conf) = @_;
421
422 my $cfspath = cfs_config_path($vmid);
423
424 PVE::Cluster::cfs_write_file($cfspath, $conf);
425}
426
9c2d4ce9
DM
427my $tempcounter = 0;
428sub write_temp_config {
429 my ($vmid, $conf) = @_;
7dfc49cc 430
9c2d4ce9
DM
431 $tempcounter++;
432 my $filename = "/tmp/temp-lxc-conf-$vmid-$$-$tempcounter.conf";
433
434 my $raw = write_lxc_config($filename, $conf);
435
436 PVE::Tools::file_set_contents($filename, $raw);
7dfc49cc 437
9c2d4ce9
DM
438 return $filename;
439}
440
d14a9a1b
DM
441# flock: we use one file handle per process, so lock file
442# can be called multiple times and succeeds for the same process.
443
444my $lock_handles = {};
445my $lockdir = "/run/lock/lxc";
446
447sub lock_filename {
448 my ($vmid) = @_;
cbb03fea 449
d14a9a1b
DM
450 return "$lockdir/pve-config-{$vmid}.lock";
451}
452
453sub lock_aquire {
454 my ($vmid, $timeout) = @_;
455
456 $timeout = 10 if !$timeout;
457 my $mode = LOCK_EX;
458
459 my $filename = lock_filename($vmid);
460
f99e8278
AD
461 mkdir $lockdir if !-d $lockdir;
462
d14a9a1b
DM
463 my $lock_func = sub {
464 if (!$lock_handles->{$$}->{$filename}) {
465 my $fh = new IO::File(">>$filename") ||
466 die "can't open file - $!\n";
467 $lock_handles->{$$}->{$filename} = { fh => $fh, refcount => 0};
468 }
469
470 if (!flock($lock_handles->{$$}->{$filename}->{fh}, $mode |LOCK_NB)) {
471 print STDERR "trying to aquire lock...";
472 my $success;
473 while(1) {
474 $success = flock($lock_handles->{$$}->{$filename}->{fh}, $mode);
475 # try again on EINTR (see bug #273)
476 if ($success || ($! != EINTR)) {
477 last;
478 }
479 }
480 if (!$success) {
481 print STDERR " failed\n";
482 die "can't aquire lock - $!\n";
483 }
484
485 $lock_handles->{$$}->{$filename}->{refcount}++;
cbb03fea 486
d14a9a1b
DM
487 print STDERR " OK\n";
488 }
489 };
490
491 eval { PVE::Tools::run_with_timeout($timeout, $lock_func); };
492 my $err = $@;
493 if ($err) {
494 die "can't lock file '$filename' - $err";
cbb03fea 495 }
d14a9a1b
DM
496}
497
498sub lock_release {
499 my ($vmid) = @_;
500
501 my $filename = lock_filename($vmid);
502
503 if (my $fh = $lock_handles->{$$}->{$filename}->{fh}) {
504 my $refcount = --$lock_handles->{$$}->{$filename}->{refcount};
505 if ($refcount <= 0) {
506 $lock_handles->{$$}->{$filename} = undef;
507 close ($fh);
508 }
509 }
510}
511
f76a2828
DM
512sub lock_container {
513 my ($vmid, $timeout, $code, @param) = @_;
514
d14a9a1b 515 my $res;
f76a2828 516
d14a9a1b
DM
517 lock_aquire($vmid, $timeout);
518 eval { $res = &$code(@param) };
519 my $err = $@;
520 lock_release($vmid);
f76a2828 521
d14a9a1b 522 die $err if $err;
f76a2828
DM
523
524 return $res;
525}
526
527my $confdesc = {
528 onboot => {
529 optional => 1,
530 type => 'boolean',
531 description => "Specifies whether a VM will be started during system bootup.",
532 default => 0,
533 },
a3249355 534 startup => get_standard_option('pve-startup-order'),
45573f7c 535 cpulimit => {
f76a2828 536 optional => 1,
8daf6a1c 537 type => 'number',
45573f7c 538 description => "Limit of CPU usage. Note if the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit.",
44da0641
DM
539 minimum => 0,
540 maximum => 128,
541 default => 0,
f76a2828
DM
542 },
543 cpuunits => {
544 optional => 1,
545 type => 'integer',
546 description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
547 minimum => 0,
548 maximum => 500000,
549 default => 1000,
550 },
551 memory => {
552 optional => 1,
553 type => 'integer',
554 description => "Amount of RAM for the VM in MB.",
555 minimum => 16,
556 default => 512,
557 },
558 swap => {
559 optional => 1,
560 type => 'integer',
561 description => "Amount of SWAP for the VM in MB.",
562 minimum => 0,
563 default => 512,
564 },
565 disk => {
566 optional => 1,
567 type => 'number',
568 description => "Amount of disk space for the VM in GB. A zero indicates no limits.",
569 minimum => 0,
10fc3ba5 570 default => 4,
f76a2828
DM
571 },
572 hostname => {
573 optional => 1,
574 description => "Set a host name for the container.",
575 type => 'string',
576 maxLength => 255,
577 },
578 description => {
579 optional => 1,
580 type => 'string',
581 description => "Container description. Only used on the configuration web interface.",
582 },
583 searchdomain => {
584 optional => 1,
585 type => 'string',
586 description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
587 },
588 nameserver => {
589 optional => 1,
590 type => 'string',
591 description => "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
592 },
ec52ac21
DM
593};
594
595my $MAX_LXC_NETWORKS = 10;
596for (my $i = 0; $i < $MAX_LXC_NETWORKS; $i++) {
597 $confdesc->{"net$i"} = {
f76a2828
DM
598 optional => 1,
599 type => 'string', format => 'pve-lxc-network',
b156e7cd 600 description => "Specifies network interfaces for the container.\n\n".
601 "The string should have the follow format:\n\n".
602 "-net<[0-9]> bridge=<vmbr<Nummber>>[,hwaddr=<MAC>]\n".
603 "[,mtu=<Number>][,name=<String>][,ip=<IPv4Format/CIDR>]\n".
604 ",ip6=<IPv6Format/CIDR>][,gw=<GatwayIPv4>]\n".
605 ",gw6=<GatwayIPv6>][,firewall=<[1|0]>][,tag=<VlanNo>]",
7dfc49cc 606 };
ec52ac21
DM
607}
608
609sub option_exists {
610 my ($name) = @_;
611
612 return defined($confdesc->{$name});
613}
f76a2828
DM
614
615# add JSON properties for create and set function
616sub json_config_properties {
617 my $prop = shift;
618
619 foreach my $opt (keys %$confdesc) {
620 $prop->{$opt} = $confdesc->{$opt};
621 }
622
623 return $prop;
624}
625
822de0c3
DM
626# container status helpers
627
628sub list_active_containers {
cbb03fea 629
822de0c3
DM
630 my $filename = "/proc/net/unix";
631
632 # similar test is used by lcxcontainers.c: list_active_containers
633 my $res = {};
cbb03fea 634
822de0c3
DM
635 my $fh = IO::File->new ($filename, "r");
636 return $res if !$fh;
637
638 while (defined(my $line = <$fh>)) {
639 if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) {
640 my $path = $1;
641 if ($path =~ m!^@/etc/pve/lxc/(\d+)/command$!) {
642 $res->{$1} = 1;
643 }
644 }
645 }
646
647 close($fh);
cbb03fea 648
822de0c3
DM
649 return $res;
650}
f76a2828 651
5c752bbf
DM
652# warning: this is slow
653sub check_running {
654 my ($vmid) = @_;
655
656 my $active_hash = list_active_containers();
657
658 return 1 if defined($active_hash->{$vmid});
cbb03fea 659
5c752bbf
DM
660 return undef;
661}
662
10fc3ba5
DM
663sub get_container_disk_usage {
664 my ($vmid) = @_;
665
666 my $cmd = ['lxc-attach', '-n', $vmid, '--', 'df', '-P', '-B', '1', '/'];
cbb03fea 667
10fc3ba5
DM
668 my $res = {
669 total => 0,
670 used => 0,
671 avail => 0,
672 };
673
674 my $parser = sub {
675 my $line = shift;
676 if (my ($fsid, $total, $used, $avail) = $line =~
677 m/^(\S+.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s.*$/) {
678 $res = {
679 total => $total,
680 used => $used,
681 avail => $avail,
682 };
683 }
684 };
685 eval { PVE::Tools::run_command($cmd, timeout => 1, outfunc => $parser); };
686 warn $@ if $@;
687
688 return $res;
689}
690
f76a2828
DM
691sub vmstatus {
692 my ($opt_vmid) = @_;
693
694 my $list = $opt_vmid ? { $opt_vmid => { type => 'lxc' }} : config_list();
695
822de0c3 696 my $active_hash = list_active_containers();
cbb03fea 697
f76a2828 698 foreach my $vmid (keys %$list) {
f76a2828 699 my $d = $list->{$vmid};
10fc3ba5
DM
700
701 my $running = defined($active_hash->{$vmid});
cbb03fea 702
10fc3ba5 703 $d->{status} = $running ? 'running' : 'stopped';
f76a2828
DM
704
705 my $cfspath = cfs_config_path($vmid);
238a56cb 706 my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
cbb03fea 707
238a56cb
DM
708 $d->{name} = $conf->{'lxc.utsname'} || "CT$vmid";
709 $d->{name} =~ s/[\s]//g;
cbb03fea 710
44da0641
DM
711 $d->{cpus} = 0;
712
713 my $cfs_period_us = $conf->{'lxc.cgroup.cpu.cfs_period_us'};
714 my $cfs_quota_us = $conf->{'lxc.cgroup.cpu.cfs_quota_us'};
715
716 if ($cfs_period_us && $cfs_quota_us) {
717 $d->{cpus} = int($cfs_quota_us/$cfs_period_us);
718 }
cbb03fea 719
238a56cb 720 $d->{disk} = 0;
10fc3ba5
DM
721 $d->{maxdisk} = defined($conf->{'pve.disksize'}) ?
722 int($conf->{'pve.disksize'}*1024*1024)*1024 : 1024*1024*1024*1024*1024;
cbb03fea 723
238a56cb 724 if (my $private = $conf->{'lxc.rootfs'}) {
10fc3ba5
DM
725 if ($private =~ m!^/!) {
726 my $res = PVE::Tools::df($private, 2);
727 $d->{disk} = $res->{used};
728 $d->{maxdisk} = $res->{total};
729 } elsif ($running) {
6ed8c6dd 730 if ($private =~ m!^(?:loop|nbd):(?:\S+)$!) {
10fc3ba5
DM
731 my $res = get_container_disk_usage($vmid);
732 $d->{disk} = $res->{used};
733 $d->{maxdisk} = $res->{total};
cbb03fea 734 }
10fc3ba5 735 }
238a56cb 736 }
cbb03fea 737
238a56cb
DM
738 $d->{mem} = 0;
739 $d->{swap} = 0;
740 $d->{maxmem} = ($conf->{'lxc.cgroup.memory.limit_in_bytes'}||0) +
44da0641 741 ($conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'}||0);
e901d418 742
238a56cb
DM
743 $d->{uptime} = 0;
744 $d->{cpu} = 0;
e901d418 745
238a56cb
DM
746 $d->{netout} = 0;
747 $d->{netin} = 0;
f76a2828 748
238a56cb
DM
749 $d->{diskread} = 0;
750 $d->{diskwrite} = 0;
f76a2828 751 }
cbb03fea 752
238a56cb
DM
753 foreach my $vmid (keys %$list) {
754 my $d = $list->{$vmid};
755 next if $d->{status} ne 'running';
f76a2828 756
22a77285
DM
757 $d->{uptime} = 100; # fixme:
758
238a56cb
DM
759 $d->{mem} = read_cgroup_value('memory', $vmid, 'memory.usage_in_bytes');
760 $d->{swap} = read_cgroup_value('memory', $vmid, 'memory.memsw.usage_in_bytes') - $d->{mem};
b5289322
AD
761
762 my $blkio_bytes = read_cgroup_value('blkio', $vmid, 'blkio.throttle.io_service_bytes', 1);
1e647c7c 763 my @bytes = split(/\n/, $blkio_bytes);
b5289322 764 foreach my $byte (@bytes) {
1e647c7c
DM
765 if (my ($key, $value) = $byte =~ /(Read|Write)\s+(\d+)/) {
766 $d->{diskread} = $2 if $key eq 'Read';
767 $d->{diskwrite} = $2 if $key eq 'Write';
768 }
b5289322 769 }
238a56cb 770 }
cbb03fea 771
f76a2828
DM
772 return $list;
773}
774
7dfc49cc
DM
775
776sub print_lxc_network {
f76a2828
DM
777 my $net = shift;
778
bedeaaf1 779 die "no network name defined\n" if !$net->{name};
f76a2828 780
bedeaaf1 781 my $res = "name=$net->{name}";
7dfc49cc 782
bedeaaf1 783 foreach my $k (qw(hwaddr mtu bridge ip gw ip6 gw6 firewall tag)) {
f76a2828
DM
784 next if !defined($net->{$k});
785 $res .= ",$k=$net->{$k}";
786 }
7dfc49cc 787
f76a2828
DM
788 return $res;
789}
790
7dfc49cc
DM
791sub parse_lxc_network {
792 my ($data) = @_;
793
794 my $res = {};
795
796 return $res if !$data;
797
798 foreach my $pv (split (/,/, $data)) {
2b1fc2ea 799 if ($pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/) {
7dfc49cc
DM
800 $res->{$1} = $2;
801 } else {
802 return undef;
803 }
804 }
805
806 $res->{type} = 'veth';
93cdbbfb 807 $res->{hwaddr} = PVE::Tools::random_ether_addr() if !$res->{hwaddr};
cbb03fea 808
7dfc49cc
DM
809 return $res;
810}
f76a2828 811
238a56cb
DM
812sub read_cgroup_value {
813 my ($group, $vmid, $name, $full) = @_;
814
815 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
816
817 return PVE::Tools::file_get_contents($path) if $full;
818
819 return PVE::Tools::file_read_firstline($path);
820}
821
bf0b8c43
AD
822sub write_cgroup_value {
823 my ($group, $vmid, $name, $value) = @_;
824
825 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
826 PVE::ProcFSTools::write_proc_entry($path, $value) if -e $path;
827
828}
829
52f1d76b
DM
830sub find_lxc_console_pids {
831
832 my $res = {};
833
834 PVE::Tools::dir_glob_foreach('/proc', '\d+', sub {
835 my ($pid) = @_;
836
837 my $cmdline = PVE::Tools::file_read_firstline("/proc/$pid/cmdline");
838 return if !$cmdline;
839
840 my @args = split(/\0/, $cmdline);
841
842 # serach for lxc-console -n <vmid>
cbb03fea 843 return if scalar(@args) != 3;
52f1d76b
DM
844 return if $args[1] ne '-n';
845 return if $args[2] !~ m/^\d+$/;
846 return if $args[0] !~ m|^(/usr/bin/)?lxc-console$|;
cbb03fea 847
52f1d76b 848 my $vmid = $args[2];
cbb03fea 849
52f1d76b
DM
850 push @{$res->{$vmid}}, $pid;
851 });
852
853 return $res;
854}
855
bedeaaf1
AD
856sub find_lxc_pid {
857 my ($vmid) = @_;
858
859 my $pid = undef;
860 my $parser = sub {
861 my $line = shift;
8b25977f 862 $pid = $1 if $line =~ m/^PID:\s+(\d+)$/;
bedeaaf1
AD
863 };
864 PVE::Tools::run_command(['lxc-info', '-n', $vmid], outfunc => $parser);
865
8b25977f 866 die "unable to get PID for CT $vmid (not running?)\n" if !$pid;
cbb03fea 867
8b25977f 868 return $pid;
bedeaaf1
AD
869}
870
55fa4e09
DM
871my $ipv4_reverse_mask = [
872 '0.0.0.0',
873 '128.0.0.0',
874 '192.0.0.0',
875 '224.0.0.0',
876 '240.0.0.0',
877 '248.0.0.0',
878 '252.0.0.0',
879 '254.0.0.0',
880 '255.0.0.0',
881 '255.128.0.0',
882 '255.192.0.0',
883 '255.224.0.0',
884 '255.240.0.0',
885 '255.248.0.0',
886 '255.252.0.0',
887 '255.254.0.0',
888 '255.255.0.0',
889 '255.255.128.0',
890 '255.255.192.0',
891 '255.255.224.0',
892 '255.255.240.0',
893 '255.255.248.0',
894 '255.255.252.0',
895 '255.255.254.0',
896 '255.255.255.0',
897 '255.255.255.128',
898 '255.255.255.192',
899 '255.255.255.224',
900 '255.255.255.240',
901 '255.255.255.248',
902 '255.255.255.252',
903 '255.255.255.254',
904 '255.255.255.255',
905];
cbb03fea
DM
906
907# Note: we cannot use Net:IP, because that only allows strict
55fa4e09
DM
908# CIDR networks
909sub parse_ipv4_cidr {
910 my ($cidr, $noerr) = @_;
911
912 if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 < 32)) {
913 return { address => $1, netmask => $ipv4_reverse_mask->[$2] };
914 }
cbb03fea 915
55fa4e09 916 return undef if $noerr;
cbb03fea 917
55fa4e09
DM
918 die "unable to parse ipv4 address/mask\n";
919}
93285df8 920
b80dd50a
DM
921sub lxc_conf_to_pve {
922 my ($vmid, $lxc_conf) = @_;
923
924 my $properties = json_config_properties();
925
926 my $conf = { digest => $lxc_conf->{digest} };
927
928 foreach my $k (keys %$properties) {
929
930 if ($k eq 'description') {
931 if (my $raw = $lxc_conf->{'pve.comment'}) {
932 $conf->{$k} = PVE::Tools::decode_text($raw);
933 }
a99b3509
DM
934 } elsif ($k eq 'onboot') {
935 $conf->{$k} = $lxc_conf->{'pve.onboot'} if $lxc_conf->{'pve.onboot'};
a3249355
DM
936 } elsif ($k eq 'startup') {
937 $conf->{$k} = $lxc_conf->{'pve.startup'} if $lxc_conf->{'pve.startup'};
b80dd50a
DM
938 } elsif ($k eq 'hostname') {
939 $conf->{$k} = $lxc_conf->{'lxc.utsname'} if $lxc_conf->{'lxc.utsname'};
ffa1d001
DM
940 } elsif ($k eq 'nameserver') {
941 $conf->{$k} = $lxc_conf->{'pve.nameserver'} if $lxc_conf->{'pve.nameserver'};
942 } elsif ($k eq 'searchdomain') {
943 $conf->{$k} = $lxc_conf->{'pve.searchdomain'} if $lxc_conf->{'pve.searchdomain'};
b80dd50a
DM
944 } elsif ($k eq 'memory') {
945 if (my $value = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'}) {
946 $conf->{$k} = int($value / (1024*1024));
947 }
948 } elsif ($k eq 'swap') {
949 if (my $value = $lxc_conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'}) {
950 my $mem = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'} || 0;
951 $conf->{$k} = int(($value -$mem) / (1024*1024));
952 }
45573f7c 953 } elsif ($k eq 'cpulimit') {
b80dd50a
DM
954 my $cfs_period_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_period_us'};
955 my $cfs_quota_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_quota_us'};
cbb03fea 956
b80dd50a 957 if ($cfs_period_us && $cfs_quota_us) {
8daf6a1c 958 $conf->{$k} = $cfs_quota_us/$cfs_period_us;
b80dd50a
DM
959 } else {
960 $conf->{$k} = 0;
961 }
962 } elsif ($k eq 'cpuunits') {
963 $conf->{$k} = $lxc_conf->{'lxc.cgroup.cpu.shares'} || 1024;
10fc3ba5
DM
964 } elsif ($k eq 'disk') {
965 $conf->{$k} = defined($lxc_conf->{'pve.disksize'}) ?
966 $lxc_conf->{'pve.disksize'} : 0;
b80dd50a
DM
967 } elsif ($k =~ m/^net\d$/) {
968 my $net = $lxc_conf->{$k};
969 next if !$net;
970 $conf->{$k} = print_lxc_network($net);
971 }
972 }
cbb03fea 973
b80dd50a
DM
974 return $conf;
975}
976
117636e5
DM
977# verify and cleanup nameserver list (replace \0 with ' ')
978sub verify_nameserver_list {
979 my ($nameserver_list) = @_;
980
981 my @list = ();
982 foreach my $server (PVE::Tools::split_list($nameserver_list)) {
983 PVE::JSONSchema::pve_verify_ip($server);
984 push @list, $server;
985 }
986
987 return join(' ', @list);
988}
989
990sub verify_searchdomain_list {
991 my ($searchdomain_list) = @_;
992
993 my @list = ();
994 foreach my $server (PVE::Tools::split_list($searchdomain_list)) {
995 # todo: should we add checks for valid dns domains?
996 push @list, $server;
997 }
998
999 return join(' ', @list);
1000}
1001
93285df8
DM
1002sub update_lxc_config {
1003 my ($vmid, $conf, $running, $param, $delete) = @_;
1004
bf0b8c43
AD
1005 my @nohotplug;
1006
cbb03fea
DM
1007 my $rootdir;
1008 if ($running) {
bedeaaf1 1009 my $pid = find_lxc_pid($vmid);
cbb03fea 1010 $rootdir = "/proc/$pid/root";
bedeaaf1
AD
1011 }
1012
93285df8
DM
1013 if (defined($delete)) {
1014 foreach my $opt (@$delete) {
1015 if ($opt eq 'hostname' || $opt eq 'memory') {
1016 die "unable to delete required option '$opt'\n";
1017 } elsif ($opt eq 'swap') {
44da0641 1018 delete $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'};
bf0b8c43 1019 write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", -1);
93285df8
DM
1020 } elsif ($opt eq 'description') {
1021 delete $conf->{'pve.comment'};
a99b3509
DM
1022 } elsif ($opt eq 'onboot') {
1023 delete $conf->{'pve.onboot'};
a3249355
DM
1024 } elsif ($opt eq 'startup') {
1025 delete $conf->{'pve.startup'};
ffa1d001
DM
1026 } elsif ($opt eq 'nameserver') {
1027 delete $conf->{'pve.nameserver'};
bf0b8c43
AD
1028 push @nohotplug, $opt;
1029 next if $running;
ffa1d001
DM
1030 } elsif ($opt eq 'searchdomain') {
1031 delete $conf->{'pve.searchdomain'};
bf0b8c43
AD
1032 push @nohotplug, $opt;
1033 next if $running;
68fba17b 1034 } elsif ($opt =~ m/^net(\d)$/) {
93285df8 1035 delete $conf->{$opt};
68fba17b
AD
1036 next if !$running;
1037 my $netid = $1;
1038 PVE::Network::veth_delete("veth${vmid}.$netid");
93285df8
DM
1039 } else {
1040 die "implement me"
1041 }
bf0b8c43 1042 PVE::LXC::write_config($vmid, $conf) if $running;
93285df8
DM
1043 }
1044 }
1045
1046 foreach my $opt (keys %$param) {
1047 my $value = $param->{$opt};
1048 if ($opt eq 'hostname') {
1049 $conf->{'lxc.utsname'} = $value;
a99b3509
DM
1050 } elsif ($opt eq 'onboot') {
1051 $conf->{'pve.onboot'} = $value ? 1 : 0;
a3249355
DM
1052 } elsif ($opt eq 'startup') {
1053 $conf->{'pve.startup'} = $value;
ffa1d001 1054 } elsif ($opt eq 'nameserver') {
117636e5 1055 my $list = verify_nameserver_list($value);
c325b32f 1056 $conf->{'pve.nameserver'} = $list;
bf0b8c43
AD
1057 push @nohotplug, $opt;
1058 next if $running;
ffa1d001 1059 } elsif ($opt eq 'searchdomain') {
117636e5 1060 my $list = verify_searchdomain_list($value);
c325b32f 1061 $conf->{'pve.searchdomain'} = $list;
bf0b8c43
AD
1062 push @nohotplug, $opt;
1063 next if $running;
93285df8
DM
1064 } elsif ($opt eq 'memory') {
1065 $conf->{'lxc.cgroup.memory.limit_in_bytes'} = $value*1024*1024;
bf0b8c43 1066 write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", $value*1024*1024);
93285df8 1067 } elsif ($opt eq 'swap') {
44da0641
DM
1068 my $mem = $conf->{'lxc.cgroup.memory.limit_in_bytes'};
1069 $mem = $param->{memory}*1024*1024 if $param->{memory};
1070 $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'} = $mem + $value*1024*1024;
bf0b8c43 1071 write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", $mem + $value*1024*1024);
3a43e41a 1072
45573f7c 1073 } elsif ($opt eq 'cpulimit') {
44da0641
DM
1074 if ($value > 0) {
1075 my $cfs_period_us = 100000;
1076 $conf->{'lxc.cgroup.cpu.cfs_period_us'} = $cfs_period_us;
1077 $conf->{'lxc.cgroup.cpu.cfs_quota_us'} = $cfs_period_us*$value;
bf0b8c43 1078 write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", $cfs_period_us*$value);
44da0641
DM
1079 } else {
1080 delete $conf->{'lxc.cgroup.cpu.cfs_period_us'};
1081 delete $conf->{'lxc.cgroup.cpu.cfs_quota_us'};
bf0b8c43 1082 write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", -1);
44da0641 1083 }
b80dd50a 1084 } elsif ($opt eq 'cpuunits') {
cbb03fea 1085 $conf->{'lxc.cgroup.cpu.shares'} = $value;
bf0b8c43 1086 write_cgroup_value("cpu", $vmid, "cpu.shares", $value);
93285df8
DM
1087 } elsif ($opt eq 'description') {
1088 $conf->{'pve.comment'} = PVE::Tools::encode_text($value);
10fc3ba5
DM
1089 } elsif ($opt eq 'disk') {
1090 $conf->{'pve.disksize'} = $value;
bf0b8c43
AD
1091 push @nohotplug, $opt;
1092 next if $running;
93285df8
DM
1093 } elsif ($opt =~ m/^net(\d+)$/) {
1094 my $netid = $1;
1095 my $net = PVE::LXC::parse_lxc_network($value);
1096 $net->{'veth.pair'} = "veth${vmid}.$netid";
cbb03fea 1097 if (!$running) {
bedeaaf1 1098 $conf->{$opt} = $net;
cbb03fea 1099 } else {
bedeaaf1
AD
1100 update_net($vmid, $conf, $opt, $net, $netid, $rootdir);
1101 }
93285df8
DM
1102 } else {
1103 die "implement me"
1104 }
bf0b8c43 1105 PVE::LXC::write_config($vmid, $conf) if $running;
93285df8 1106 }
bf0b8c43 1107
5cfa0567
DM
1108 if ($running && scalar(@nohotplug)) {
1109 die "unable to modify " . join(',', @nohotplug) . " while container is running\n";
1110 }
93285df8 1111}
c325b32f
DM
1112
1113sub get_primary_ips {
1114 my ($conf) = @_;
1115
1116 # return data from net0
cbb03fea 1117
c325b32f
DM
1118 my $net = $conf->{net0};
1119 return undef if !$net;
1120
1121 my $ipv4 = $net->{ip};
1122 $ipv4 =~ s!/\d+$!! if $ipv4;
1123 my $ipv6 = $net->{ip};
1124 $ipv6 =~ s!/\d+$!! if $ipv6;
cbb03fea 1125
c325b32f
DM
1126 return ($ipv4, $ipv6);
1127}
148d1cb4
DM
1128
1129sub destory_lxc_container {
1130 my ($storage_cfg, $vmid, $conf) = @_;
1131
1132 if (my $volid = $conf->{'pve.volid'}) {
1133
1134 my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $volid);
1135 die "got strange volid (containe is not owner!)\n" if $vmid != $owner;
1136
1137 PVE::Storage::vdisk_free($storage_cfg, $volid);
1138
1139 destroy_config($vmid);
1140
1141 } else {
1142 my $cmd = ['lxc-destroy', '-n', $vmid ];
1143
1144 PVE::Tools::run_command($cmd);
1145 }
1146}
68fba17b 1147
93cdbbfb
AD
1148my $safe_num_ne = sub {
1149 my ($a, $b) = @_;
1150
1151 return 0 if !defined($a) && !defined($b);
1152 return 1 if !defined($a);
1153 return 1 if !defined($b);
1154
1155 return $a != $b;
1156};
1157
1158my $safe_string_ne = sub {
1159 my ($a, $b) = @_;
1160
1161 return 0 if !defined($a) && !defined($b);
1162 return 1 if !defined($a);
1163 return 1 if !defined($b);
1164
1165 return $a ne $b;
1166};
1167
1168sub update_net {
bedeaaf1 1169 my ($vmid, $conf, $opt, $newnet, $netid, $rootdir) = @_;
93cdbbfb
AD
1170
1171 my $veth = $newnet->{'veth.pair'};
cbb03fea 1172 my $vethpeer = $veth . "p";
93cdbbfb
AD
1173 my $eth = $newnet->{name};
1174
bedeaaf1
AD
1175 if ($conf->{$opt}) {
1176 if (&$safe_string_ne($conf->{$opt}->{hwaddr}, $newnet->{hwaddr}) ||
cbb03fea 1177 &$safe_string_ne($conf->{$opt}->{name}, $newnet->{name})) {
93cdbbfb
AD
1178
1179 PVE::Network::veth_delete($veth);
bedeaaf1
AD
1180 delete $conf->{$opt};
1181 PVE::LXC::write_config($vmid, $conf);
93cdbbfb 1182
bedeaaf1
AD
1183 hotplug_net($vmid, $conf, $opt, $newnet);
1184
1185 } elsif (&$safe_string_ne($conf->{$opt}->{bridge}, $newnet->{bridge}) ||
1186 &$safe_num_ne($conf->{$opt}->{tag}, $newnet->{tag}) ||
1187 &$safe_num_ne($conf->{$opt}->{firewall}, $newnet->{firewall})) {
1188
1189 if ($conf->{$opt}->{bridge}){
1190 PVE::Network::tap_unplug($veth);
1191 delete $conf->{$opt}->{bridge};
1192 delete $conf->{$opt}->{tag};
1193 delete $conf->{$opt}->{firewall};
1194 PVE::LXC::write_config($vmid, $conf);
1195 }
93cdbbfb 1196
93cdbbfb 1197 PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
bedeaaf1
AD
1198 $conf->{$opt}->{bridge} = $newnet->{bridge} if $newnet->{bridge};
1199 $conf->{$opt}->{tag} = $newnet->{tag} if $newnet->{tag};
1200 $conf->{$opt}->{firewall} = $newnet->{firewall} if $newnet->{firewall};
1201 PVE::LXC::write_config($vmid, $conf);
93cdbbfb
AD
1202 }
1203 } else {
bedeaaf1 1204 hotplug_net($vmid, $conf, $opt, $newnet);
93cdbbfb
AD
1205 }
1206
bedeaaf1 1207 update_ipconfig($vmid, $conf, $opt, $eth, $newnet, $rootdir);
93cdbbfb
AD
1208}
1209
1210sub hotplug_net {
bedeaaf1 1211 my ($vmid, $conf, $opt, $newnet) = @_;
93cdbbfb
AD
1212
1213 my $veth = $newnet->{'veth.pair'};
cbb03fea 1214 my $vethpeer = $veth . "p";
93cdbbfb
AD
1215 my $eth = $newnet->{name};
1216
1217 PVE::Network::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
1218 PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
1219
cbb03fea 1220 # attach peer in container
93cdbbfb
AD
1221 my $cmd = ['lxc-device', '-n', $vmid, 'add', $vethpeer, "$eth" ];
1222 PVE::Tools::run_command($cmd);
1223
cbb03fea 1224 # link up peer in container
93cdbbfb
AD
1225 $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'link', 'set', $eth ,'up' ];
1226 PVE::Tools::run_command($cmd);
bedeaaf1
AD
1227
1228 $conf->{$opt}->{type} = 'veth';
1229 $conf->{$opt}->{bridge} = $newnet->{bridge} if $newnet->{bridge};
1230 $conf->{$opt}->{tag} = $newnet->{tag} if $newnet->{tag};
1231 $conf->{$opt}->{firewall} = $newnet->{firewall} if $newnet->{firewall};
1232 $conf->{$opt}->{hwaddr} = $newnet->{hwaddr} if $newnet->{hwaddr};
1233 $conf->{$opt}->{name} = $newnet->{name} if $newnet->{name};
1234 $conf->{$opt}->{'veth.pair'} = $newnet->{'veth.pair'} if $newnet->{'veth.pair'};
1235
1236 delete $conf->{$opt}->{ip} if $conf->{$opt}->{ip};
1237 delete $conf->{$opt}->{ip6} if $conf->{$opt}->{ip6};
1238 delete $conf->{$opt}->{gw} if $conf->{$opt}->{gw};
1239 delete $conf->{$opt}->{gw6} if $conf->{$opt}->{gw6};
1240
1241 PVE::LXC::write_config($vmid, $conf);
93cdbbfb
AD
1242}
1243
68a05bb3 1244sub update_ipconfig {
bedeaaf1
AD
1245 my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
1246
1247 my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir);
1248
2bfd1615
DM
1249 my $update_gateway;
1250 if (&$safe_string_ne($conf->{$opt}->{gw}, $newnet->{gw})) {
1251
1252 $update_gateway = 1;
1253 if ($conf->{$opt}->{gw}) {
1254 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'del', 'default', 'via', $conf->{$opt}->{gw} ];
1255 eval { PVE::Tools::run_command($cmd); };
1256 warn $@ if $@; # ignore errors here
1257 delete $conf->{$opt}->{gw};
1258 PVE::LXC::write_config($vmid, $conf);
1259 $lxc_setup->setup_network($conf);
1260 }
1261 }
1262
bedeaaf1
AD
1263 if (&$safe_string_ne($conf->{$opt}->{ip}, $newnet->{ip})) {
1264
1265 if ($conf->{$opt}->{ip}) {
1266 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'del', $conf->{$opt}->{ip}, 'dev', $eth ];
2bfd1615
DM
1267 eval { PVE::Tools::run_command($cmd); };
1268 warn $@ if $@; # ignore errors here
bedeaaf1
AD
1269 delete $conf->{$opt}->{ip};
1270 PVE::LXC::write_config($vmid, $conf);
1271 $lxc_setup->setup_network($conf);
1272 }
1273
1274 if ($newnet->{ip}) {
1275 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'add', $newnet->{ip}, 'dev', $eth ];
1276 PVE::Tools::run_command($cmd);
68a05bb3 1277
bedeaaf1
AD
1278 $conf->{$opt}->{ip} = $newnet->{ip};
1279 PVE::LXC::write_config($vmid, $conf);
1280 $lxc_setup->setup_network($conf);
1281 }
1282 }
1283
2bfd1615 1284 if ($update_gateway) {
bedeaaf1
AD
1285
1286 if ($newnet->{gw}) {
1287 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'add', 'default', 'via', $newnet->{gw} ];
1288 PVE::Tools::run_command($cmd);
1289
1290 $conf->{$opt}->{gw} = $newnet->{gw};
1291 PVE::LXC::write_config($vmid, $conf);
1292 $lxc_setup->setup_network($conf);
1293 }
1294 }
1295
2bfd1615
DM
1296 my $update_gateway6;
1297 if (&$safe_string_ne($conf->{$opt}->{gw6}, $newnet->{gw6})) {
1298
1299 $update_gateway6 = 1;
1300 if ($conf->{$opt}->{gw6}) {
1301 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'del', 'default', 'via', $conf->{$opt}->{gw6} ];
1302 eval { PVE::Tools::run_command($cmd); };
1303 warn $@ if $@; # ignore errors here
1304 delete $conf->{$opt}->{gw6};
1305 PVE::LXC::write_config($vmid, $conf);
1306 $lxc_setup->setup_network($conf);
1307 }
61ae66e4 1308 }
2bfd1615 1309
bedeaaf1 1310 if (&$safe_string_ne($conf->{$opt}->{ip6}, $newnet->{ip6})) {
68a05bb3 1311
bedeaaf1
AD
1312 if ($conf->{$opt}->{ip6}) {
1313 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'del', $conf->{$opt}->{ip6}, 'dev', $eth ];
2bfd1615
DM
1314 eval { PVE::Tools::run_command($cmd); };
1315 warn $@ if $@; # ignore errors here
bedeaaf1
AD
1316 delete $conf->{$opt}->{ip6};
1317 PVE::LXC::write_config($vmid, $conf);
1318 $lxc_setup->setup_network($conf);
1319 }
1320
1321 if ($newnet->{ip6}) {
1322 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'add', $newnet->{ip6}, 'dev', $eth ];
1323 PVE::Tools::run_command($cmd);
1324
1325 $conf->{$opt}->{ip6} = $newnet->{ip6};
1326 PVE::LXC::write_config($vmid, $conf);
1327 $lxc_setup->setup_network($conf);
1328 }
68a05bb3
AD
1329 }
1330
2bfd1615 1331 if ($update_gateway6) {
bedeaaf1
AD
1332
1333 if ($newnet->{gw6}) {
1334 my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'add', 'default', 'via', $newnet->{gw6} ];
1335 PVE::Tools::run_command($cmd);
1336
1337 $conf->{$opt}->{gw6} = $newnet->{gw6};
1338 PVE::LXC::write_config($vmid, $conf);
1339 $lxc_setup->setup_network($conf);
1340 }
68a05bb3 1341 }
68a05bb3
AD
1342}
1343
f76a2828 13441;