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