]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXC.pm
conside searchdomain when updating /etc/hosts
[pve-container.git] / src / PVE / LXC.pm
CommitLineData
f76a2828
DM
1package PVE::LXC;
2
3use strict;
4use warnings;
5
6use File::Path;
7use Fcntl ':flock';
8
9use PVE::Cluster qw(cfs_register_file cfs_read_file);
10use PVE::SafeSyslog;
11use PVE::INotify;
a3249355 12use PVE::JSONSchema qw(get_standard_option);
55fa4e09 13use PVE::Tools qw($IPV6RE $IPV4RE);
f76a2828
DM
14
15use Data::Dumper;
16
17cfs_register_file('/lxc/', \&parse_lxc_config, \&write_lxc_config);
18
7dfc49cc
DM
19PVE::JSONSchema::register_format('pve-lxc-network', \&verify_lxc_network);
20sub verify_lxc_network {
21 my ($value, $noerr) = @_;
22
23 return $value if parse_lxc_network($value);
24
25 return undef if $noerr;
26
27 die "unable to parse network setting\n";
28}
29
f76a2828
DM
30my $nodename = PVE::INotify::nodename();
31
822de0c3
DM
32sub parse_lxc_size {
33 my ($name, $value) = @_;
34
35 if ($value =~ m/^(\d+)(b|k|m|g)?$/i) {
36 my ($res, $unit) = ($1, lc($2 || 'b'));
37
38 return $res if $unit eq 'b';
39 return $res*1024 if $unit eq 'k';
40 return $res*1024*1024 if $unit eq 'm';
41 return $res*1024*1024*1024 if $unit eq 'g';
42 }
43
44 return undef;
45}
46
f76a2828 47my $valid_lxc_keys = {
822de0c3 48 'lxc.arch' => 'i386|x86|i686|x86_64|amd64',
f76a2828
DM
49 'lxc.include' => 1,
50 'lxc.rootfs' => 1,
51 'lxc.mount' => 1,
52 'lxc.utsname' => 1,
53
54664cd3 54 'lxc.id_map' => 1,
822de0c3
DM
55
56 'lxc.cgroup.memory.limit_in_bytes' => \&parse_lxc_size,
44da0641 57 'lxc.cgroup.memory.memsw.limit_in_bytes' => \&parse_lxc_size,
b80dd50a
DM
58 'lxc.cgroup.cpu.cfs_period_us' => '\d+',
59 'lxc.cgroup.cpu.cfs_quota_us' => '\d+',
60 'lxc.cgroup.cpu.shares' => '\d+',
822de0c3 61
54664cd3
DM
62 # mount related
63 'lxc.mount' => 1,
64 'lxc.mount.entry' => 1,
65 'lxc.mount.auto' => 1,
66
67 # not used by pve
b80dd50a 68 'lxc.tty' => '\d+',
54664cd3
DM
69 'lxc.pts' => 1,
70 'lxc.haltsignal' => 1,
71 'lxc.rebootsignal' => 1,
72 'lxc.stopsignal' => 1,
73 'lxc.init_cmd' => 1,
74 'lxc.console' => 1,
75 'lxc.console.logfile' => 1,
76 'lxc.devttydir' => 1,
77 'lxc.autodev' => 1,
78 'lxc.kmsg' => 1,
79 'lxc.cap.drop' => 1,
80 'lxc.cap.keep' => 1,
81 'lxc.aa_profile' => 1,
82 'lxc.aa_allow_incomplete' => 1,
83 'lxc.se_context' => 1,
84 'lxc.loglevel' => 1,
85 'lxc.logfile' => 1,
86 'lxc.environment' => 1,
87
88
89 # autostart
90 'lxc.start.auto' => 1,
91 'lxc.start.delay' => 1,
92 'lxc.start.order' => 1,
93 'lxc.group' => 1,
94
95 # hooks
96 'lxc.hook.pre-start' => 1,
97 'lxc.hook.pre-mount' => 1,
98 'lxc.hook.mount' => 1,
99 'lxc.hook.autodev' => 1,
100 'lxc.hook.start' => 1,
101 'lxc.hook.post-stop' => 1,
102 'lxc.hook.clone' => 1,
103
f76a2828 104 # pve related keys
ffa1d001
DM
105 'pve.nameserver' => 1,
106 'pve.searchdomain' => 1,
a99b3509 107 'pve.onboot' => '(0|1)',
a3249355
DM
108 'pve.startup' => sub {
109 my ($name, $value) = @_;
110 return PVE::JSONSchema::pve_verify_startup_order($value);
111 },
f76a2828
DM
112 'pve.comment' => 1,
113};
114
93285df8 115my $valid_lxc_network_keys = {
f76a2828 116 type => 1,
f76a2828
DM
117 mtu => 1,
118 name => 1, # ifname inside container
119 'veth.pair' => 1, # ifname at host (eth${vmid}.X)
120 hwaddr => 1,
93285df8
DM
121};
122
123my $valid_pve_network_keys = {
8a04b6c7 124 bridge => 1,
2b1fc2ea
DM
125 tag => 1,
126 firewall => 1,
93285df8
DM
127 ip => 1,
128 gw => 1,
129 ip6 => 1,
130 gw6 => 1,
f76a2828
DM
131};
132
133my $lxc_array_configs = {
134 'lxc.network' => 1,
135 'lxc.mount' => 1,
136 'lxc.include' => 1,
137};
138
139sub write_lxc_config {
140 my ($filename, $data) = @_;
141
142 my $raw = "";
143
144 return $raw if !$data;
145
146 my $done_hash = { digest => 1};
7dfc49cc 147
f76a2828
DM
148 foreach my $k (sort keys %$data) {
149 next if $k !~ m/^lxc\./;
150 $done_hash->{$k} = 1;
151 $raw .= "$k = $data->{$k}\n";
152 }
7dfc49cc 153
f76a2828 154 foreach my $k (sort keys %$data) {
fff3a342
DM
155 next if $k !~ m/^pve\./;
156 $done_hash->{$k} = 1;
157 $raw .= "$k = $data->{$k}\n";
158 }
159
160 foreach my $k (sort keys %$data) {
f76a2828
DM
161 next if $k !~ m/^net\d+$/;
162 $done_hash->{$k} = 1;
163 my $net = $data->{$k};
164 $raw .= "lxc.network.type = $net->{type}\n";
165 foreach my $subkey (sort keys %$net) {
166 next if $subkey eq 'type';
93285df8
DM
167 if ($valid_lxc_network_keys->{$subkey}) {
168 $raw .= "lxc.network.$subkey = $net->{$subkey}\n";
169 } elsif ($valid_pve_network_keys->{$subkey}) {
170 $raw .= "pve.network.$subkey = $net->{$subkey}\n";
171 } else {
172 die "found invalid network key '$subkey'";
173 }
f76a2828
DM
174 }
175 }
176
177 foreach my $k (sort keys %$data) {
178 next if $done_hash->{$k};
179 die "found un-written value in config - implement this!";
180 }
181
f76a2828
DM
182 return $raw;
183}
184
822de0c3
DM
185sub parse_lxc_option {
186 my ($name, $value) = @_;
187
188 my $parser = $valid_lxc_keys->{$name};
189
190 die "inavlid key '$name'\n" if !defined($parser);
191
192 if ($parser eq '1') {
193 return $value;
194 } elsif (ref($parser)) {
195 my $res = &$parser($name, $value);
196 return $res if defined($res);
197 } else {
198 # assume regex
199 return $value if $value =~ m/^$parser$/;
200 }
201
202 die "unable to parse value '$value' for option '$name'\n";
203}
204
f76a2828
DM
205sub parse_lxc_config {
206 my ($filename, $raw) = @_;
207
208 return undef if !defined($raw);
209
210 my $data = {
211 digest => Digest::SHA::sha1_hex($raw),
212 };
213
214 $filename =~ m|/lxc/(\d+)/config$|
215 || die "got strange filename '$filename'";
216
217 my $vmid = $1;
218
219 my $network_counter = 0;
220 my $network_list = [];
221 my $host_ifnames = {};
222
223 my $find_next_hostif_name = sub {
224 for (my $i = 0; $i < 10; $i++) {
225 my $name = "veth${vmid}.$i";
226 if (!$host_ifnames->{$name}) {
227 $host_ifnames->{$name} = 1;
228 return $name;
229 }
230 }
231
232 die "unable to find free host_ifname"; # should not happen
233 };
7dfc49cc 234
f76a2828
DM
235 my $push_network = sub {
236 my ($netconf) = @_;
237 return if !$netconf;
238 push @{$network_list}, $netconf;
239 $network_counter++;
240 if (my $netname = $netconf->{'veth.pair'}) {
241 if ($netname =~ m/^veth(\d+).(\d)$/) {
242 die "wrong vmid for network interface pair\n" if $1 != $vmid;
243 my $host_ifnames->{$netname} = 1;
244 } else {
245 die "wrong network interface pair\n";
246 }
247 }
248 };
249
250 my $network;
7dfc49cc 251
f76a2828
DM
252 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
253 my $line = $1;
254
255 next if $line =~ m/^\#/;
256 next if $line =~ m/^\s*$/;
257
258 if ($line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
259 my ($subkey, $value) = ($1, $2);
260 if ($subkey eq 'type') {
261 &$push_network($network);
262 $network = { type => $value };
93285df8
DM
263 } elsif ($valid_lxc_network_keys->{$subkey}) {
264 $network->{$subkey} = $value;
265 } else {
266 die "unable to parse config line: $line\n";
267 }
268 next;
269 }
270 if ($line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
271 my ($subkey, $value) = ($1, $2);
272 if ($valid_pve_network_keys->{$subkey}) {
f76a2828
DM
273 $network->{$subkey} = $value;
274 } else {
275 die "unable to parse config line: $line\n";
276 }
f76a2828
DM
277 next;
278 }
279 if ($line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/) {
280 my ($name, $value) = ($1, $2);
281 $data->{$name} = $value;
282 next;
283 }
54664cd3 284 if ($line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/) {
f76a2828
DM
285 my ($name, $value) = ($1, $2);
286
f76a2828
DM
287 die "multiple definitions for $name\n" if defined($data->{$name});
288
822de0c3 289 $data->{$name} = parse_lxc_option($name, $value);
f76a2828
DM
290 next;
291 }
292
293 die "unable to parse config line: $line\n";
294 }
295
296 &$push_network($network);
297
298 foreach my $net (@{$network_list}) {
299 $net->{'veth.pair'} = &$find_next_hostif_name() if !$net->{'veth.pair'};
300 $net->{hwaddr} = PVE::Tools::random_ether_addr() if !$net->{hwaddr};
301 die "unsupported network type '$net->{type}'\n" if $net->{type} ne 'veth';
302
303 if ($net->{'veth.pair'} =~ m/^veth\d+.(\d+)$/) {
304 $data->{"net$1"} = $net;
305 }
7dfc49cc
DM
306 }
307
308 return $data;
f76a2828
DM
309}
310
311sub config_list {
312 my $vmlist = PVE::Cluster::get_vmlist();
313 my $res = {};
314 return $res if !$vmlist || !$vmlist->{ids};
315 my $ids = $vmlist->{ids};
316
317 foreach my $vmid (keys %$ids) {
318 next if !$vmid; # skip CT0
319 my $d = $ids->{$vmid};
320 next if !$d->{node} || $d->{node} ne $nodename;
321 next if !$d->{type} || $d->{type} ne 'lxc';
322 $res->{$vmid}->{type} = 'lxc';
323 }
324 return $res;
325}
326
327sub cfs_config_path {
328 my ($vmid, $node) = @_;
329
330 $node = $nodename if !$node;
331 return "nodes/$node/lxc/$vmid/config";
332}
333
9c2d4ce9
DM
334sub config_file {
335 my ($vmid, $node) = @_;
336
337 my $cfspath = cfs_config_path($vmid, $node);
338 return "/etc/pve/$cfspath";
339}
340
f76a2828
DM
341sub load_config {
342 my ($vmid) = @_;
343
344 my $cfspath = cfs_config_path($vmid);
345
346 my $conf = PVE::Cluster::cfs_read_file($cfspath);
347 die "container $vmid does not exists\n" if !defined($conf);
348
349 return $conf;
350}
351
352sub write_config {
353 my ($vmid, $conf) = @_;
354
355 my $cfspath = cfs_config_path($vmid);
356
357 PVE::Cluster::cfs_write_file($cfspath, $conf);
358}
359
9c2d4ce9
DM
360my $tempcounter = 0;
361sub write_temp_config {
362 my ($vmid, $conf) = @_;
7dfc49cc 363
9c2d4ce9
DM
364 $tempcounter++;
365 my $filename = "/tmp/temp-lxc-conf-$vmid-$$-$tempcounter.conf";
366
367 my $raw = write_lxc_config($filename, $conf);
368
369 PVE::Tools::file_set_contents($filename, $raw);
7dfc49cc 370
9c2d4ce9
DM
371 return $filename;
372}
373
f76a2828
DM
374sub lock_container {
375 my ($vmid, $timeout, $code, @param) = @_;
376
377 my $lockdir = "/run/lock/lxc";
378 my $lockfile = "$lockdir/pve-config-{$vmid}.lock";
379
380 File::Path::make_path($lockdir);
381
382 my $res = PVE::Tools::lock_file($lockfile, $timeout, $code, @param);
383
384 die $@ if $@;
385
386 return $res;
387}
388
389my $confdesc = {
390 onboot => {
391 optional => 1,
392 type => 'boolean',
393 description => "Specifies whether a VM will be started during system bootup.",
394 default => 0,
395 },
a3249355 396 startup => get_standard_option('pve-startup-order'),
f76a2828
DM
397 cpus => {
398 optional => 1,
399 type => 'integer',
44da0641
DM
400 description => "The number of CPUs for this container (0 is unlimited).",
401 minimum => 0,
402 maximum => 128,
403 default => 0,
f76a2828
DM
404 },
405 cpuunits => {
406 optional => 1,
407 type => 'integer',
408 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.",
409 minimum => 0,
410 maximum => 500000,
411 default => 1000,
412 },
413 memory => {
414 optional => 1,
415 type => 'integer',
416 description => "Amount of RAM for the VM in MB.",
417 minimum => 16,
418 default => 512,
419 },
420 swap => {
421 optional => 1,
422 type => 'integer',
423 description => "Amount of SWAP for the VM in MB.",
424 minimum => 0,
425 default => 512,
426 },
427 disk => {
428 optional => 1,
429 type => 'number',
430 description => "Amount of disk space for the VM in GB. A zero indicates no limits.",
431 minimum => 0,
432 default => 2,
433 },
434 hostname => {
435 optional => 1,
436 description => "Set a host name for the container.",
437 type => 'string',
438 maxLength => 255,
439 },
440 description => {
441 optional => 1,
442 type => 'string',
443 description => "Container description. Only used on the configuration web interface.",
444 },
445 searchdomain => {
446 optional => 1,
447 type => 'string',
448 description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
449 },
450 nameserver => {
451 optional => 1,
452 type => 'string',
453 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.",
454 },
ec52ac21
DM
455};
456
457my $MAX_LXC_NETWORKS = 10;
458for (my $i = 0; $i < $MAX_LXC_NETWORKS; $i++) {
459 $confdesc->{"net$i"} = {
f76a2828
DM
460 optional => 1,
461 type => 'string', format => 'pve-lxc-network',
462 description => "Specifies network interfaces for the container.",
7dfc49cc 463 };
ec52ac21
DM
464}
465
466sub option_exists {
467 my ($name) = @_;
468
469 return defined($confdesc->{$name});
470}
f76a2828
DM
471
472# add JSON properties for create and set function
473sub json_config_properties {
474 my $prop = shift;
475
476 foreach my $opt (keys %$confdesc) {
477 $prop->{$opt} = $confdesc->{$opt};
478 }
479
480 return $prop;
481}
482
822de0c3
DM
483# container status helpers
484
485sub list_active_containers {
486
487 my $filename = "/proc/net/unix";
488
489 # similar test is used by lcxcontainers.c: list_active_containers
490 my $res = {};
491
492 my $fh = IO::File->new ($filename, "r");
493 return $res if !$fh;
494
495 while (defined(my $line = <$fh>)) {
496 if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) {
497 my $path = $1;
498 if ($path =~ m!^@/etc/pve/lxc/(\d+)/command$!) {
499 $res->{$1} = 1;
500 }
501 }
502 }
503
504 close($fh);
505
506 return $res;
507}
f76a2828 508
5c752bbf
DM
509# warning: this is slow
510sub check_running {
511 my ($vmid) = @_;
512
513 my $active_hash = list_active_containers();
514
515 return 1 if defined($active_hash->{$vmid});
516
517 return undef;
518}
519
f76a2828
DM
520sub vmstatus {
521 my ($opt_vmid) = @_;
522
523 my $list = $opt_vmid ? { $opt_vmid => { type => 'lxc' }} : config_list();
524
822de0c3
DM
525 my $active_hash = list_active_containers();
526
f76a2828 527 foreach my $vmid (keys %$list) {
f76a2828 528 my $d = $list->{$vmid};
822de0c3 529 $d->{status} = $active_hash->{$vmid} ? 'running' : 'stopped';
f76a2828
DM
530
531 my $cfspath = cfs_config_path($vmid);
238a56cb
DM
532 my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
533
534 $d->{name} = $conf->{'lxc.utsname'} || "CT$vmid";
535 $d->{name} =~ s/[\s]//g;
e901d418 536
44da0641
DM
537 $d->{cpus} = 0;
538
539 my $cfs_period_us = $conf->{'lxc.cgroup.cpu.cfs_period_us'};
540 my $cfs_quota_us = $conf->{'lxc.cgroup.cpu.cfs_quota_us'};
541
542 if ($cfs_period_us && $cfs_quota_us) {
543 $d->{cpus} = int($cfs_quota_us/$cfs_period_us);
544 }
238a56cb
DM
545
546 $d->{disk} = 0;
547 $d->{maxdisk} = 1;
548 if (my $private = $conf->{'lxc.rootfs'}) {
549 my $res = PVE::Tools::df($private, 2);
550 $d->{disk} = $res->{used};
551 $d->{maxdisk} = $res->{total};
552 }
553
554 $d->{mem} = 0;
555 $d->{swap} = 0;
556 $d->{maxmem} = ($conf->{'lxc.cgroup.memory.limit_in_bytes'}||0) +
44da0641 557 ($conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'}||0);
e901d418 558
238a56cb
DM
559 $d->{uptime} = 0;
560 $d->{cpu} = 0;
e901d418 561
238a56cb
DM
562 $d->{netout} = 0;
563 $d->{netin} = 0;
f76a2828 564
238a56cb
DM
565 $d->{diskread} = 0;
566 $d->{diskwrite} = 0;
f76a2828 567 }
238a56cb
DM
568
569 foreach my $vmid (keys %$list) {
570 my $d = $list->{$vmid};
571 next if $d->{status} ne 'running';
f76a2828 572
22a77285
DM
573 $d->{uptime} = 100; # fixme:
574
238a56cb
DM
575 $d->{mem} = read_cgroup_value('memory', $vmid, 'memory.usage_in_bytes');
576 $d->{swap} = read_cgroup_value('memory', $vmid, 'memory.memsw.usage_in_bytes') - $d->{mem};
577 }
578
f76a2828
DM
579 return $list;
580}
581
7dfc49cc
DM
582
583sub print_lxc_network {
f76a2828
DM
584 my $net = shift;
585
8a04b6c7 586 die "no network bridge defined\n" if !$net->{bridge};
f76a2828 587
8a04b6c7 588 my $res = "bridge=$net->{bridge}";
7dfc49cc 589
2b1fc2ea 590 foreach my $k (qw(hwaddr mtu name ip gw ip6 gw6 firewall tag)) {
f76a2828
DM
591 next if !defined($net->{$k});
592 $res .= ",$k=$net->{$k}";
593 }
7dfc49cc 594
f76a2828
DM
595 return $res;
596}
597
7dfc49cc
DM
598sub parse_lxc_network {
599 my ($data) = @_;
600
601 my $res = {};
602
603 return $res if !$data;
604
605 foreach my $pv (split (/,/, $data)) {
2b1fc2ea 606 if ($pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/) {
7dfc49cc
DM
607 $res->{$1} = $2;
608 } else {
609 return undef;
610 }
611 }
612
613 $res->{type} = 'veth';
614 $res->{hwaddr} = PVE::Tools::random_ether_addr() if !$res->{mac};
615
616 return $res;
617}
f76a2828 618
238a56cb
DM
619sub read_cgroup_value {
620 my ($group, $vmid, $name, $full) = @_;
621
622 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
623
624 return PVE::Tools::file_get_contents($path) if $full;
625
626 return PVE::Tools::file_read_firstline($path);
627}
628
52f1d76b
DM
629sub find_lxc_console_pids {
630
631 my $res = {};
632
633 PVE::Tools::dir_glob_foreach('/proc', '\d+', sub {
634 my ($pid) = @_;
635
636 my $cmdline = PVE::Tools::file_read_firstline("/proc/$pid/cmdline");
637 return if !$cmdline;
638
639 my @args = split(/\0/, $cmdline);
640
641 # serach for lxc-console -n <vmid>
642 return if scalar(@args) != 3;
643 return if $args[1] ne '-n';
644 return if $args[2] !~ m/^\d+$/;
645 return if $args[0] !~ m|^(/usr/bin/)?lxc-console$|;
646
647 my $vmid = $args[2];
648
649 push @{$res->{$vmid}}, $pid;
650 });
651
652 return $res;
653}
654
55fa4e09
DM
655my $ipv4_reverse_mask = [
656 '0.0.0.0',
657 '128.0.0.0',
658 '192.0.0.0',
659 '224.0.0.0',
660 '240.0.0.0',
661 '248.0.0.0',
662 '252.0.0.0',
663 '254.0.0.0',
664 '255.0.0.0',
665 '255.128.0.0',
666 '255.192.0.0',
667 '255.224.0.0',
668 '255.240.0.0',
669 '255.248.0.0',
670 '255.252.0.0',
671 '255.254.0.0',
672 '255.255.0.0',
673 '255.255.128.0',
674 '255.255.192.0',
675 '255.255.224.0',
676 '255.255.240.0',
677 '255.255.248.0',
678 '255.255.252.0',
679 '255.255.254.0',
680 '255.255.255.0',
681 '255.255.255.128',
682 '255.255.255.192',
683 '255.255.255.224',
684 '255.255.255.240',
685 '255.255.255.248',
686 '255.255.255.252',
687 '255.255.255.254',
688 '255.255.255.255',
689];
690
691# Note: we cannot use Net:IP, because that only allows strict
692# CIDR networks
693sub parse_ipv4_cidr {
694 my ($cidr, $noerr) = @_;
695
696 if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 < 32)) {
697 return { address => $1, netmask => $ipv4_reverse_mask->[$2] };
698 }
699
700 return undef if $noerr;
701
702 die "unable to parse ipv4 address/mask\n";
703}
93285df8 704
b80dd50a
DM
705sub lxc_conf_to_pve {
706 my ($vmid, $lxc_conf) = @_;
707
708 my $properties = json_config_properties();
709
710 my $conf = { digest => $lxc_conf->{digest} };
711
712 foreach my $k (keys %$properties) {
713
714 if ($k eq 'description') {
715 if (my $raw = $lxc_conf->{'pve.comment'}) {
716 $conf->{$k} = PVE::Tools::decode_text($raw);
717 }
a99b3509
DM
718 } elsif ($k eq 'onboot') {
719 $conf->{$k} = $lxc_conf->{'pve.onboot'} if $lxc_conf->{'pve.onboot'};
a3249355
DM
720 } elsif ($k eq 'startup') {
721 $conf->{$k} = $lxc_conf->{'pve.startup'} if $lxc_conf->{'pve.startup'};
b80dd50a
DM
722 } elsif ($k eq 'hostname') {
723 $conf->{$k} = $lxc_conf->{'lxc.utsname'} if $lxc_conf->{'lxc.utsname'};
ffa1d001
DM
724 } elsif ($k eq 'nameserver') {
725 $conf->{$k} = $lxc_conf->{'pve.nameserver'} if $lxc_conf->{'pve.nameserver'};
726 } elsif ($k eq 'searchdomain') {
727 $conf->{$k} = $lxc_conf->{'pve.searchdomain'} if $lxc_conf->{'pve.searchdomain'};
b80dd50a
DM
728 } elsif ($k eq 'memory') {
729 if (my $value = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'}) {
730 $conf->{$k} = int($value / (1024*1024));
731 }
732 } elsif ($k eq 'swap') {
733 if (my $value = $lxc_conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'}) {
734 my $mem = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'} || 0;
735 $conf->{$k} = int(($value -$mem) / (1024*1024));
736 }
737 } elsif ($k eq 'cpus') {
738 my $cfs_period_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_period_us'};
739 my $cfs_quota_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_quota_us'};
740
741 if ($cfs_period_us && $cfs_quota_us) {
742 $conf->{$k} = int($cfs_quota_us/$cfs_period_us);
743 } else {
744 $conf->{$k} = 0;
745 }
746 } elsif ($k eq 'cpuunits') {
747 $conf->{$k} = $lxc_conf->{'lxc.cgroup.cpu.shares'} || 1024;
748 } elsif ($k =~ m/^net\d$/) {
749 my $net = $lxc_conf->{$k};
750 next if !$net;
751 $conf->{$k} = print_lxc_network($net);
752 }
753 }
754
755 return $conf;
756}
757
93285df8
DM
758sub update_lxc_config {
759 my ($vmid, $conf, $running, $param, $delete) = @_;
760
761 # fixme: hotplug
762 die "unable to modify config while container is running\n" if $running;
763
764 if (defined($delete)) {
765 foreach my $opt (@$delete) {
766 if ($opt eq 'hostname' || $opt eq 'memory') {
767 die "unable to delete required option '$opt'\n";
768 } elsif ($opt eq 'swap') {
44da0641 769 delete $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'};
93285df8
DM
770 } elsif ($opt eq 'description') {
771 delete $conf->{'pve.comment'};
a99b3509
DM
772 } elsif ($opt eq 'onboot') {
773 delete $conf->{'pve.onboot'};
a3249355
DM
774 } elsif ($opt eq 'startup') {
775 delete $conf->{'pve.startup'};
ffa1d001
DM
776 } elsif ($opt eq 'nameserver') {
777 delete $conf->{'pve.nameserver'};
778 } elsif ($opt eq 'searchdomain') {
779 delete $conf->{'pve.searchdomain'};
93285df8
DM
780 } elsif ($opt =~ m/^net\d$/) {
781 delete $conf->{$opt};
782 } else {
783 die "implement me"
784 }
785 }
786 }
787
788 foreach my $opt (keys %$param) {
789 my $value = $param->{$opt};
790 if ($opt eq 'hostname') {
791 $conf->{'lxc.utsname'} = $value;
a99b3509
DM
792 } elsif ($opt eq 'onboot') {
793 $conf->{'pve.onboot'} = $value ? 1 : 0;
a3249355
DM
794 } elsif ($opt eq 'startup') {
795 $conf->{'pve.startup'} = $value;
ffa1d001
DM
796 } elsif ($opt eq 'nameserver') {
797 $conf->{'pve.nameserver'} = $value;
798 } elsif ($opt eq 'searchdomain') {
799 $conf->{'pve.searchdomain'} = $value;
93285df8
DM
800 } elsif ($opt eq 'memory') {
801 $conf->{'lxc.cgroup.memory.limit_in_bytes'} = $value*1024*1024;
802 } elsif ($opt eq 'swap') {
44da0641
DM
803 my $mem = $conf->{'lxc.cgroup.memory.limit_in_bytes'};
804 $mem = $param->{memory}*1024*1024 if $param->{memory};
805 $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'} = $mem + $value*1024*1024;
806 } elsif ($opt eq 'cpus') {
807 if ($value > 0) {
808 my $cfs_period_us = 100000;
809 $conf->{'lxc.cgroup.cpu.cfs_period_us'} = $cfs_period_us;
810 $conf->{'lxc.cgroup.cpu.cfs_quota_us'} = $cfs_period_us*$value;
811 } else {
812 delete $conf->{'lxc.cgroup.cpu.cfs_period_us'};
813 delete $conf->{'lxc.cgroup.cpu.cfs_quota_us'};
814 }
b80dd50a
DM
815 } elsif ($opt eq 'cpuunits') {
816 $conf->{'lxc.cgroup.cpu.shares'} = $value;
93285df8
DM
817 } elsif ($opt eq 'description') {
818 $conf->{'pve.comment'} = PVE::Tools::encode_text($value);
819 } elsif ($opt =~ m/^net(\d+)$/) {
820 my $netid = $1;
821 my $net = PVE::LXC::parse_lxc_network($value);
822 $net->{'veth.pair'} = "veth${vmid}.$netid";
823 $conf->{$opt} = $net;
824 } else {
825 die "implement me"
826 }
827 }
828}
238a56cb 829
f76a2828 8301;