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