]> git.proxmox.com Git - pve-manager.git/blame - PVE/OpenVZ.pm
encrypt container password (sha1)
[pve-manager.git] / PVE / OpenVZ.pm
CommitLineData
aff192e6
DM
1package PVE::OpenVZ;
2
3use strict;
339e4159
DM
4use LockFile::Simple;
5use File::stat qw();
6use POSIX qw (LONG_MAX);
aff192e6
DM
7use IO::Dir;
8use IO::File;
339e4159
DM
9use PVE::Tools qw(extract_param);
10use PVE::ProcFSTools;
11use PVE::Cluster qw(cfs_register_file cfs_read_file);
12use PVE::SafeSyslog;
13use PVE::INotify;
14use PVE::JSONSchema;
e761984e
DM
15use Digest::SHA1;
16use Encode;
aff192e6 17
339e4159
DM
18my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
19my $nodename = PVE::INotify::nodename();
aff192e6 20
339e4159
DM
21sub config_list {
22 my $vmlist = PVE::Cluster::get_vmlist();
23 my $res = {};
24 return $res if !$vmlist || !$vmlist->{ids};
25 my $ids = $vmlist->{ids};
aff192e6 26
339e4159
DM
27 foreach my $vmid (keys %$ids) {
28 next if !$vmid; # skip VE0
29 my $d = $ids->{$vmid};
30 next if !$d->{node} || $d->{node} ne $nodename;
31 next if !$d->{type} || $d->{type} ne 'openvz';
32 $res->{$vmid}->{type} = 'openvz';
33 }
34 return $res;
35}
aff192e6 36
339e4159
DM
37sub cfs_config_path {
38 my ($vmid, $node) = @_;
aff192e6 39
339e4159
DM
40 $node = $nodename if !$node;
41 return "nodes/$node/openvz/$vmid.conf";
42}
aff192e6 43
07151796
DM
44sub config_file {
45 my ($vmid, $node) = @_;
46
47 my $cfspath = cfs_config_path($vmid, $node);
48 return "/etc/pve/$cfspath";
49}
c3163e37
DM
50
51sub load_config {
52 my ($vmid) = @_;
53
07151796 54 my $cfspath = cfs_config_path($vmid);
c3163e37 55
07151796
DM
56 my $conf = PVE::Cluster::cfs_read_file($cfspath);
57 die "container $vmid does not exists\n" if !defined($conf);
c3163e37 58
07151796 59 return $conf;
c3163e37
DM
60}
61
339e4159 62my $last_proc_vestat = {};
aff192e6 63
339e4159 64sub vmstatus {
07151796 65 my ($opt_vmid) = @_;
aff192e6 66
339e4159 67 my $list = config_list();
aff192e6 68
339e4159 69 foreach my $vmid (keys %$list) {
07151796
DM
70 next if $opt_vmid && ($vmid ne $opt_vmid);
71
339e4159
DM
72 my $d = $list->{$vmid};
73 $d->{status} = 'stopped';
aff192e6 74
339e4159
DM
75 my $cfspath = cfs_config_path($vmid);
76 if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) {
77 $d->{name} = $conf->{hostname}->{value} || "VM$vmid";
78 $d->{name} =~ s/[\s]//g;
aff192e6 79
339e4159 80 $d->{cpus} = $conf->{cpus}->{value} || 1;
aff192e6 81
339e4159
DM
82 $d->{disk} = 0;
83 $d->{maxdisk} = int($conf->{diskspace}->{bar} * 1024);
aff192e6 84
339e4159
DM
85 $d->{mem} = 0;
86 $d->{maxmem} = int((($conf->{physpages}->{lim} + $conf->{swappages}->{lim})* 4096));
87 $d->{nproc} = 0;
aff192e6 88
339e4159 89 $d->{uptime} = 0;
c486db9d 90 $d->{cpu} = 0;
339e4159
DM
91 $d->{relcpu} = 0;
92
c486db9d
DM
93 $d->{netout} = 0;
94 $d->{netin} = 0;
95
96 $d->{diskread} = 0;
97 $d->{diskwrite} = 0;
98
339e4159
DM
99 if (my $ip = $conf->{ip_address}->{value}) {
100 $ip =~ s/,;/ /g;
101 $d->{ip} = (split(/\s+/, $ip))[0];
102 } else {
103 $d->{ip} = '-';
aff192e6
DM
104 }
105 }
106 }
107
339e4159 108 if (my $fh = IO::File->new ("/proc/mounts", "r")) {
aff192e6
DM
109 while (defined (my $line = <$fh>)) {
110 if ($line =~ m|^/var/lib/vz/private/(\d+)\s+/var/lib/vz/root/|) {
339e4159 111 $list->{$1}->{status} = 'mounted' if defined($list->{$1});
aff192e6
DM
112 }
113 }
339e4159 114 close($fh);
aff192e6
DM
115 }
116
339e4159
DM
117 if (my $fh = IO::File->new ("/proc/user_beancounters", "r")) {
118 my $vmid;
aff192e6
DM
119 while (defined (my $line = <$fh>)) {
120 if ($line =~ m|\s*((\d+):\s*)?([a-z]+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$|) {
339e4159
DM
121 $vmid = $2 if defined($2);
122 next if !$vmid;
aff192e6 123 my ($name, $held, $maxheld, $bar, $lim, $failcnt) = ($3, $4, $5, $6, $7, $8);
339e4159
DM
124 if (my $d = $list->{$vmid}) {
125 if ($name eq 'physpages') {
126 $d->{mem} += int($held * 4096);
127 } elsif ($name eq 'swappages') {
128 $d->{mem} += int($held * 4096);
aff192e6
DM
129 } elsif ($name eq 'numproc') {
130 $d->{nproc} = $held;
131 }
132 }
133 }
134 }
339e4159 135 close($fh);
aff192e6
DM
136 }
137
339e4159 138 if (my $fh = IO::File->new ("/proc/vz/vzquota", "r")) {
aff192e6
DM
139 while (defined (my $line = <$fh>)) {
140 if ($line =~ m|^(\d+):\s+/var/lib/vz/private/\d+$|) {
339e4159 141 if (my $d = $list->{$1}) {
aff192e6
DM
142 $line = <$fh>;
143 if ($line =~ m|^\s*1k-blocks\s+(\d+)\s+(\d+)\s|) {
339e4159
DM
144 $d->{disk} = int ($1 * 1024);
145 $d->{maxdisk} = int ($2 * 1024);
aff192e6
DM
146 }
147 }
148 }
149 }
339e4159 150 close($fh);
aff192e6
DM
151 }
152
aff192e6 153 my $cpus = $cpuinfo->{cpus} || 1;
339e4159
DM
154 # Note: OpenVZ does not use POSIX::_SC_CLK_TCK
155 my $hz = 1000;
aff192e6
DM
156
157 # see http://wiki.openvz.org/Vestat
339e4159 158 if (my $fh = new IO::File ("/proc/vz/vestat", "r")) {
aff192e6
DM
159 while (defined (my $line = <$fh>)) {
160 if ($line =~ m/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
339e4159 161 my $vmid = $1;
aff192e6
DM
162 my $user = $2;
163 my $nice = $3;
164 my $system = $4;
165 my $ut = $5;
166 my $sum = $8*$cpus; # uptime in jiffies * cpus = available jiffies
167 my $used = $9; # used time in jiffies
168
339e4159 169 my $uptime = int ($ut / $hz);
aff192e6 170
339e4159 171 my $d = $list->{$vmid};
aff192e6
DM
172 next if !$d;
173
174 $d->{status} = 'running';
175 $d->{uptime} = $uptime;
176
339e4159
DM
177 if (!defined ($last_proc_vestat->{$vmid}) ||
178 ($last_proc_vestat->{$vmid}->{sum} > $sum)) {
c486db9d 179 $last_proc_vestat->{$vmid} = { used => 0, sum => 0, cpu => 0, relcpu => 0};
aff192e6
DM
180 }
181
339e4159 182 my $diff = $sum - $last_proc_vestat->{$vmid}->{sum};
aff192e6
DM
183
184 if ($diff > 1000) { # don't update too often
339e4159 185 my $useddiff = $used - $last_proc_vestat->{$vmid}->{used};
c486db9d 186 my $cpu = int ($useddiff*100/$diff);
339e4159
DM
187 $last_proc_vestat->{$vmid}->{sum} = $sum;
188 $last_proc_vestat->{$vmid}->{used} = $used;
c486db9d 189 $last_proc_vestat->{$vmid}->{cpu} = $d->{cpu} = $cpu;
aff192e6 190
c486db9d 191 my $relcpu = $cpu;
339e4159 192 $last_proc_vestat->{$vmid}->{relcpu} = $d->{relcpu} = $relcpu;
aff192e6
DM
193
194 } else {
c486db9d 195 $d->{cpu} = $last_proc_vestat->{$vmid}->{cpu};
339e4159 196 $d->{relcpu} = $last_proc_vestat->{$vmid}->{relcpu};
aff192e6
DM
197 }
198 }
199 }
339e4159
DM
200 close($fh);
201 }
202
203 return $list;
204
205}
206
207my $confdesc = {
208 onboot => {
209 optional => 1,
210 type => 'boolean',
211 description => "Specifies whether a VM will be started during system bootup.",
212 default => 0,
213 },
214 cpus => {
215 optional => 1,
216 type => 'integer',
217 description => "The number of CPUs for this container.",
218 minimum => 1,
219 default => 1,
220 },
221 cpuunits => {
222 optional => 1,
223 type => 'integer',
224 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.",
225 minimum => 0,
226 maximum => 500000,
227 default => 1000,
228 },
229 memory => {
230 optional => 1,
231 type => 'integer',
232 description => "Amount of RAM for the VM in MB.",
233 minimum => 16,
234 default => 512,
235 },
236 swap => {
237 optional => 1,
238 type => 'integer',
239 description => "Amount of SWAP for the VM in MB.",
240 minimum => 16,
241 default => 512,
242 },
243 disk => {
244 optional => 1,
245 type => 'number',
c3163e37
DM
246 description => "Amount of disk space for the VM in GB. A zero indicates no limits.",
247 minimum => 0,
339e4159
DM
248 default => 2,
249 },
250 quotatime => {
251 optional => 1,
252 type => 'integer',
253 description => "Set quota grace period (seconds).",
254 minimum => 0,
255 default => 0,
256 },
257 quotaugidlimit => {
258 optional => 1,
259 type => 'integer',
260 description => "Set maximum number of user/group IDs in a container for which disk quota inside the container will be accounted. If this value is set to 0, user and group quotas inside the container will not.",
261 minimum => 0,
262 default => 0,
263 },
264 hostname => {
265 optional => 1,
266 description => "Set a host name for the container.",
267 type => 'string',
268 maxLength => 255,
269 },
270 description => {
271 optional => 1,
272 type => 'string',
273 description => "Container description. Only used on the configuration web interface.",
274 },
275 searchdomain => {
276 optional => 1,
277 type => 'string',
4223bcba 278 description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
339e4159
DM
279 },
280 nameserver => {
281 optional => 1,
282 type => 'string',
4223bcba 283 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.",
339e4159
DM
284 },
285 ip_address => {
286 optional => 1,
287 type => 'string',
288 description => "Specifies the address the container will be assigned.",
289 },
290 netif => {
291 optional => 1,
292 type => 'string', format => 'pve-openvz-netif',
293 description => "Specifies network interfaces for the container.",
294 },
295};
296
297# add JSON properties for create and set function
298sub json_config_properties {
299 my $prop = shift;
300
301 foreach my $opt (keys %$confdesc) {
302 $prop->{$opt} = $confdesc->{$opt};
303 }
304
305 return $prop;
306}
307
308# read global vz.conf
309my $read_global_vz_config = sub {
310
311 my $res = {
312 rootdir => '/var/lib/vz/root/$VEID', # note '$VEID' is a place holder
313 privatedir => '/var/lib/vz/private/$VEID', # note '$VEID' is a place holder
a408dfd7 314 dumpdir => '/var/lib/vz/dump',
339e4159
DM
315 lockdir => '/var/lib/vz/lock',
316 };
317
a408dfd7
DM
318 my $filename = "/etc/vz/vz.conf";
319
320 return $res if ! -f $filename;
321
322 my $data = PVE::Tools::file_get_contents($filename);
339e4159
DM
323
324 if ($data =~ m/^\s*VE_PRIVATE=(.*)$/m) {
325 my $dir = $1;
326 $dir =~ s/^\"(.*)\"/$1/;
327 if ($dir !~ m/\$VEID/) {
328 warn "VE_PRIVATE does not contain '\$VEID' ('$dir')\n";
329 } else {
330 $res->{privatedir} = $dir;
331 }
332 }
333 if ($data =~ m/^\s*VE_ROOT=(.*)$/m) {
334 my $dir = $1;
335 $dir =~ s/^\"(.*)\"/$1/;
336 if ($dir !~ m/\$VEID/) {
337 warn "VE_ROOT does not contain '\$VEID' ('$dir')\n";
338 } else {
339 $res->{rootdir} = $dir;
340 }
341 }
342 if ($data =~ m/^\s*DUMPDIR=(.*)$/m) {
343 my $dir = $1;
344 $dir =~ s/^\"(.*)\"/$1/;
345 $dir =~ s|/\$VEID$||;
346 $res->{dumpdir} = $dir;
347 }
348 if ($data =~ m/^\s*LOCKDIR=(.*)$/m) {
349 my $dir = $1;
350 $dir =~ s/^\"(.*)\"/$1/;
351 $res->{lockdir} = $dir;
aff192e6
DM
352 }
353
354 return $res;
339e4159
DM
355};
356
357my $global_vzconf = &$read_global_vz_config();
358my $res_unlimited = LONG_MAX;
359
360sub parse_netif {
361 my ($data) = @_;
362
363 my $res = {};
364 return $res if !$data;
365
366 foreach my $iface (split (/;/, $data)) {
367 my $d = {};
368 foreach my $pv (split (/,/, $iface)) {
369 if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac)=(.+)$/) {
370 $d->{$1} = $2;
371 }
372 }
373 if ($d->{ifname}) {
374 $d->{raw} = $data;
375 $res->{$d->{ifname}} = $d;
376 } else {
377 return undef;
378 }
379 }
380
381 return $res;
382}
383
384PVE::JSONSchema::register_format('pve-openvz-netif', \&verify_netif);
385sub verify_netif {
386 my ($value, $noerr) = @_;
387
388 return $value if parse_netif($value);
389
390 return undef if $noerr;
391
392 die "unable to parse --netif value";
393}
394
395sub parse_res_num_ignore {
396 my ($key, $text) = @_;
397
398 if ($text =~ m/^(\d+|unlimited)(:.*)?$/) {
399 return { bar => $1 eq 'unlimited' ? $res_unlimited : $1 };
400 }
401
402 return undef;
403}
404
405sub parse_res_num_num {
406 my ($key, $text) = @_;
407
408 if ($text =~ m/^(\d+|unlimited)(:(\d+|unlimited))?$/) {
409 my $res = { bar => $1 eq 'unlimited' ? $res_unlimited : $1 };
410 if (defined($3)) {
411 $res->{lim} = $3 eq 'unlimited' ? $res_unlimited : $3;
412 } else {
413 $res->{lim} = $res->{bar};
414 }
415 return $res;
416 }
417
418 return undef;
419}
420
421sub parse_res_bar_limit {
422 my ($text, $base) = @_;
423
424 return $res_unlimited if $text eq 'unlimited';
425
426 if ($text =~ m/^(\d+)([TGMKP])?$/i) {
427 my $val = $1;
428 my $mult = lc($2);
429 if ($mult eq 'k') {
430 $val = $val * 1024;
431 } elsif ($mult eq 'm') {
432 $val = $val * 1024 * 1024;
433 } elsif ($mult eq 'g') {
434 $val = $val * 1024 * 1024 * 1024;
435 } elsif ($mult eq 't') {
436 $val = $val * 1024 * 1024 * 1024 * 1024;
437 } elsif ($mult eq 'p') {
438 $val = $val * 4096;
439 } else {
440 return $val;
441 }
442 return int($val/$base);
443 }
444
445 return undef;
446}
447
448sub parse_res_bytes_bytes {
449 my ($key, $text) = @_;
450
451 my @a = split(/:/, $text);
452 $a[1] = $a[0] if !defined($a[1]);
453
454 my $bar = parse_res_bar_limit($a[0], 1);
455 my $lim = parse_res_bar_limit($a[1], 1);
456
457 if (defined($bar) && defined($lim)) {
458 return { bar => $bar, lim => $lim };
459 }
460
461 return undef;
462}
463
464sub parse_res_block_block {
465 my ($key, $text) = @_;
466
467 my @a = split(/:/, $text);
468 $a[1] = $a[0] if !defined($a[1]);
469
470 my $bar = parse_res_bar_limit($a[0], 1024);
471 my $lim = parse_res_bar_limit($a[1], 1024);
472
473 if (defined($bar) && defined($lim)) {
474 return { bar => $bar, lim => $lim };
475 }
476
477 return undef;
478}
479
480sub parse_res_pages_pages {
481 my ($key, $text) = @_;
482
483 my @a = split(/:/, $text);
484 $a[1] = $a[0] if !defined($a[1]);
485
486 my $bar = parse_res_bar_limit($a[0], 4096);
487 my $lim = parse_res_bar_limit($a[1], 4096);
488
489 if (defined($bar) && defined($lim)) {
490 return { bar => $bar, lim => $lim };
491 }
492
493 return undef;
494}
495
496sub parse_res_pages_unlimited {
497 my ($key, $text) = @_;
498
499 my @a = split(/:/, $text);
500
501 my $bar = parse_res_bar_limit($a[0], 4096);
502
503 if (defined($bar)) {
504 return { bar => $bar, lim => $res_unlimited };
505 }
506
507 return undef;
508}
509
510sub parse_res_pages_ignore {
511 my ($key, $text) = @_;
512
513 my @a = split(/:/, $text);
514
515 my $bar = parse_res_bar_limit($a[0], 4096);
516
517 if (defined($bar)) {
518 return { bar => $bar };
519 }
520
521 return undef;
522}
523
524sub parse_res_ignore_pages {
525 my ($key, $text) = @_;
526
527 my @a = split(/:/, $text);
528 $a[1] = $a[0] if !defined($a[1]);
529
530 my $lim = parse_res_bar_limit($a[1] , 4096);
531
532 if (defined($lim)) {
533 return { bar => 0, lim => $lim };
534 }
535
536 return undef;
537}
538
539sub parse_boolean {
540 my ($key, $text) = @_;
541
542 return { value => "yes" } if $text =~ m/^(yes|true|on|1)$/i;
543 return { value => "no" } if $text =~ m/^(no|false|off|0)$/i;
544
545 return undef;
546};
547
548sub parse_integer {
549 my ($key, $text) = @_;
550
551 if ($text =~ m/^(\d+)$/) {
552 return { value => int($1) };
553 }
554
555 return undef;
556};
557
558my $ovz_ressources = {
559 numproc => \&parse_res_num_ignore,
560 numtcpsock => \&parse_res_num_ignore,
561 numothersock => \&parse_res_num_ignore,
562 numfile => \&parse_res_num_ignore,
563 numflock => \&parse_res_num_num,
564 numpty => \&parse_res_num_ignore,
565 numsiginfo => \&parse_res_num_ignore,
566 numiptent => \&parse_res_num_ignore,
567
568 vmguarpages => \&parse_res_pages_unlimited,
569 oomguarpages => \&parse_res_pages_unlimited,
570 lockedpages => \&parse_res_pages_ignore,
571 privvmpages => \&parse_res_pages_pages,
572 shmpages => \&parse_res_pages_ignore,
573 physpages => \&parse_res_pages_pages,
574 swappages => \&parse_res_ignore_pages,
575
576 kmemsize => \&parse_res_bytes_bytes,
577 tcpsndbuf => \&parse_res_bytes_bytes,
578 tcprcvbuf => \&parse_res_bytes_bytes,
579 othersockbuf => \&parse_res_bytes_bytes,
580 dgramrcvbuf => \&parse_res_bytes_bytes,
581 dcachesize => \&parse_res_bytes_bytes,
582
583 diskquota => \&parse_boolean,
584 diskspace => \&parse_res_block_block,
585 diskinodes => \&parse_res_num_num,
586 quotatime => \&parse_integer,
587 quotaugidlimit => \&parse_integer,
588
589 cpuunits => \&parse_integer,
590 cpulimit => \&parse_integer,
591 cpus => \&parse_integer,
592 cpumask => 'string',
593 meminfo => 'string',
594 iptables => 'string',
595
596 ip_address => 'string',
597 netif => 'string',
598 hostname => 'string',
599 nameserver => 'string',
600 searchdomain => 'string',
601
602 name => 'string',
603 description => 'string',
604 onboot => \&parse_boolean,
605 initlog => \&parse_boolean,
606 bootorder => \&parse_integer,
607 ostemplate => 'string',
608 ve_root => 'string',
609 ve_private => 'string',
610 disabled => \&parse_boolean,
611 origin_sample => 'string',
612 noatime => \&parse_boolean,
613 capability => 'string',
614 devnodes => 'string',
615 devices => 'string',
616 pci => 'string',
617 features => 'string',
618 ioprio => \&parse_integer,
619
620};
621
622sub parse_ovz_config {
623 my ($filename, $raw) = @_;
624
625 return undef if !defined($raw);
626
627 my $data = {
628 digest => Digest::SHA1::sha1_hex($raw),
629 };
630
631 $filename =~ m|/openvz/(\d+)\.conf$|
632 || die "got strange filename '$filename'";
633
634 my $vmid = $1;
635
636 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
637 my $line = $1;
638
639 next if $line =~ m/^\#/;
640 next if $line =~ m/^\s*$/;
641
642 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
643 my $name = lc($1);
644 my $text = $2;
645
646 my $parser = $ovz_ressources->{$name};
647 if (!$parser || !ref($parser)) {
648 $data->{$name}->{value} = $text;
649 next;
650 } else {
651 if (my $res = &$parser($name, $text)) {
652 $data->{$name} = $res;
653 next;
654 }
655 }
656 }
657 die "unable to parse config line: $line\n";
658 }
659
660 return $data;
661}
662
663cfs_register_file('/openvz/', \&parse_ovz_config);
664
665sub format_res_value {
666 my ($key, $value) = @_;
667
668 return 'unlimited' if $value == $res_unlimited;
669
670 return 0 if $value == 0;
671
672 if ($key =~ m/pages$/) {
673 my $bytes = $value * 4096;
674 my $mb = int ($bytes / (1024 * 1024));
675 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
676 } elsif ($key =~ m/space$/) {
677 my $bytes = $value * 1024;
678 my $gb = int ($bytes / (1024 * 1024 * 1024));
679 return "${gb}G" if $gb * 1024 * 1024 * 1024 == $bytes;
680 my $mb = int ($bytes / (1024 * 1024));
681 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
682 } elsif ($key =~ m/size$/) {
683 my $bytes = $value;
684 my $mb = int ($bytes / (1024 * 1024));
685 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
686 }
aff192e6 687
339e4159 688 return $value;
aff192e6 689}
339e4159
DM
690
691sub format_res_bar_lim {
692 my ($key, $data) = @_;
693
694 if (defined($data->{lim}) && ($data->{lim} ne $data->{bar})) {
695 return format_res_value($key, $data->{bar}) . ":" . format_res_value($key, $data->{lim});
696 } else {
697 return format_res_value($key, $data->{bar});
698 }
699}
700
701sub create_config_line {
702 my ($key, $data) = @_;
703
704 my $text;
705
706 if (defined($data->{value})) {
707 $text .= uc($key) . "=\"$data->{value}\"\n";
708 } elsif (defined($data->{bar})) {
709 my $tmp = format_res_bar_lim($key, $data);
710 $text .= uc($key) . "=\"$tmp\"\n";
711 }
712}
713
714sub update_ovz_config {
715 my ($veconf, $param) = @_;
716
717 my $changes = [];
718
719 # test if barrier or limit changed
720 my $push_bl_changes = sub {
721 my ($name, $bar, $lim) = @_;
722
723 my $old = format_res_bar_lim($name, $veconf->{$name});
724 my $new = format_res_bar_lim($name, { bar => $bar, lim => $lim });
725 if ($old ne $new) {
726 $veconf->{$name}->{bar} = $bar;
727 $veconf->{$name}->{lim} = $lim;
728 push @$changes, "--$name", $new;
729 }
730 };
731
c3163e37
DM
732 my $mem = $veconf->{physpages}->{lim} ?
733 int (($veconf->{physpages}->{lim} * 4) / 1024) : 512;
734 my $swap = $veconf->{swappages}->{lim} ?
735 int (($veconf->{swappages}->{lim} * 4) / 1024) : 0;
339e4159 736
c3163e37 737 my $disk = ($veconf->{diskspace}->{bar} || $res_unlimited) / (1024*1024);
339e4159
DM
738 my $cpuunits = $veconf->{cpuunits}->{value} || 1000;
739 my $quotatime = $veconf->{quotatime}->{value} || 0;
740 my $quotaugidlimit = $veconf->{quotaugidlimit}->{value} || 0;
741 my $cpus = $veconf->{cpus}->{value} || 1;
742
743 if ($param->{memory}) {
744 $mem = $param->{memory};
745 }
746
747 if (defined ($param->{swap})) {
748 $swap = $param->{swap};
749 }
750
751 if ($param->{disk}) {
752 $disk = $param->{disk};
753 }
754
755 if ($param->{cpuunits}) {
756 $cpuunits = $param->{cpuunits};
757 }
758
759 if (defined($param->{quotatime})) {
760 $quotatime = $param->{quotatime};
761 }
762
763 if (defined($param->{quotaugidlimit})) {
764 $quotaugidlimit = $param->{quotaugidlimit};
765 }
766
767 if ($param->{cpus}) {
768 $cpus = $param->{cpus};
769 }
770
771 # memory related parameter
772
773 &$push_bl_changes('vmguarpages', 0, $res_unlimited);
774 &$push_bl_changes('oomguarpages', 0, $res_unlimited);
775 &$push_bl_changes('privvmpages', $res_unlimited, $res_unlimited);
776
777 # lock half of $mem
778 my $lockedpages = int($mem*1024/8);
779 &$push_bl_changes('lockedpages', $lockedpages, undef);
780
781 my $kmemsize = int($mem/2);
782 &$push_bl_changes('kmemsize', int($kmemsize/1.1)*1024*1024, $kmemsize*1024*1024);
783
784 my $dcachesize = int($mem/4);
785 &$push_bl_changes('dcachesize', int($dcachesize/1.1)*1024*1024, $dcachesize*1024*1024);
786
787 my $physpages = int($mem*1024/4);
788 &$push_bl_changes('physpages', 0, $physpages);
789
790 my $swappages = int($swap*1024/4);
791 &$push_bl_changes('swappages', 0, $swappages);
792
793
794 # disk quota parameters
c3163e37 795 if (!$disk || ($disk * 1.1) >= ($res_unlimited / (1024 * 1024))) {
339e4159
DM
796 &$push_bl_changes('diskspace', $res_unlimited, $res_unlimited);
797 &$push_bl_changes('diskinodes', $res_unlimited, $res_unlimited);
798 } else {
799 my $diskspace = int ($disk * 1024 * 1024);
800 my $diskspace_lim = int ($diskspace * 1.1);
801 &$push_bl_changes('diskspace', $diskspace, $diskspace_lim);
802 my $diskinodes = int ($disk * 200000);
803 my $diskinodes_lim = int ($disk * 220000);
804 &$push_bl_changes('diskinodes', $diskinodes, $diskinodes_lim);
805 }
806 if ($veconf->{'quotatime'}->{value} != $quotatime) {
807 $veconf->{'quotatime'}->{value} = $quotatime;
808 push @$changes, '--quotatime', "$quotatime";
809 }
810
811 if ($veconf->{'quotaugidlimit'}->{value} != $quotaugidlimit) {
812 $veconf->{'quotaugidlimit'}->{value} = $quotaugidlimit;
813 push @$changes, '--quotaugidlimit', "$quotaugidlimit";
814 }
815
816 # cpu settings
817
818 if ($veconf->{'cpuunits'}->{value} != $cpuunits) {
819 $veconf->{'cpuunits'}->{value} = $cpuunits;
820 push @$changes, '--cpuunits', "$cpuunits";
821 }
822
823 if ($veconf->{'cpus'}->{value} != $cpus) {
824 $veconf->{'cpus'}->{value} = $cpus;
825 push @$changes, '--cpus', "$cpus";
826 }
827
828 my $cond_set_boolean = sub {
829 my ($name) = @_;
830
831 return if !defined($param->{$name});
832
833 my $newvalue = $param->{$name} ? 'yes' : 'no';
834 my $oldvalue = $veconf->{$name}->{value};
835 if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
836 $veconf->{$name}->{value} = $newvalue;
837 push @$changes, "--$name", $newvalue;
838 }
839 };
840
841 my $cond_set_value = sub {
842 my ($name, $newvalue) = @_;
843
844 $newvalue = defined($newvalue) ? $newvalue : $param->{$name};
845 return if !defined($newvalue);
846
847 my $oldvalue = $veconf->{$name}->{value};
848 if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
849 $veconf->{$name}->{value} = $newvalue;
850 push @$changes, "--$name", $newvalue;
851 }
852 };
853
854 &$cond_set_boolean('onboot');
855
856 &$cond_set_value('hostname');
857
858 &$cond_set_value('searchdomain');
859
860 if ($param->{'description'}) {
861 &$cond_set_value('description', PVE::Tools::encode_text($param->{'description'}));
862 }
863
864 if (defined($param->{ip_address})) {
865 my $iphash = {};
866 if (defined($veconf->{'ip_address'}) && $veconf->{'ip_address'}->{value}) {
867 foreach my $ip (split (/\s+/, $veconf->{ip_address}->{value})) {
868 $iphash->{$ip} = 1;
869 }
870 }
871 my $newhash = {};
872 foreach my $ip (PVE::Tools::split_list($param->{'ip_address'})) {
873 next if $ip !~ m|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d+)?$|;
874 $newhash->{$ip} = 1;
875 if (!$iphash->{$ip}) {
876 push @$changes, '--ipadd', $ip;
877 $iphash->{$ip} = 1; # only add once
878 }
879 }
880 foreach my $ip (keys %$iphash) {
881 if (!$newhash->{$ip}) {
882 push @$changes, '--ipdel', $ip;
883 }
884 }
885 $veconf->{'ip_address'}->{value} = join(' ', keys %$iphash);
886 }
887
888 if (defined($param->{netif})) {
889 my $ifaces = {};
890 if (defined ($veconf->{netif}) && $veconf->{netif}->{value}) {
891 $ifaces = parse_netif($veconf->{netif}->{value});
892 }
893 my $newif = parse_netif($param->{netif});
894
895 foreach my $ifname (sort keys %$ifaces) {
896 if (!$newif->{$ifname}) {
897 push @$changes, '--netif_del', $ifname;
898 }
899 }
900
901 my $newvalue = '';
902 foreach my $ifname (sort keys %$newif) {
903 $newvalue .= ';' if $newvalue;
904 $newvalue .= $ifname;
905 $newvalue .= $newif->{$ifname}->{mac} ? ",$newif->{$ifname}->{mac}" : ',';
906 $newvalue .= $newif->{$ifname}->{host_ifname} ? ",$newif->{$ifname}->{host_ifname}" : ',';
907 $newvalue .= $newif->{$ifname}->{host_mac} ? ",$newif->{$ifname}->{host_mac}" : ',';
908 $newvalue .= $newif->{$ifname}->{bridge} ? ",$newif->{$ifname}->{bridge}" : '';
909
910 if (!$ifaces->{$ifname} || ($ifaces->{$ifname}->{raw} ne $newif->{$ifname}->{raw})) {
911 push @$changes, '--netif_add', $newvalue;
912 }
913 }
914 $veconf->{netif}->{value} = $newvalue;
915 }
916
917 if (defined($param->{'nameserver'})) {
918 my $nshash = {};
919 foreach my $ns (PVE::Tools::split_list($param->{'nameserver'})) {
920 if (!$nshash->{$ns}) {
921 push @$changes, '--nameserver', $ns;
922 $nshash->{$ns} = 1;
923 }
924 }
925 $veconf->{'nameserver'}->{value} = join(' ', keys %$nshash);
926 }
927
9020f201 928 # foreach my $nv (@$changes) { print "CHANGE: $nv\n"; }
339e4159
DM
929
930 return $changes;
931}
932
933sub generate_raw_config {
934 my ($raw, $conf) = @_;
935
936 my $text = '';
937
938 my $found = {};
939
940 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
941 my $line = $1;
942
943 if ($line =~ m/^\#/ || $line =~ m/^\s*$/) {
944 $text .= "$line\n";
945 next;
946 }
947
948 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
949 my $name = lc($1);
950 if ($conf->{$name}) {
951 $found->{$name} = 1;
952 if (my $line = create_config_line($name, $conf->{$name})) {
953 $text .= $line;
954 }
955 }
956 }
957 }
958
959 foreach my $key (keys %$conf) {
960 next if $found->{$key};
961 next if $key eq 'digest';
962 if (my $line = create_config_line($key, $conf->{$key})) {
963 $text .= $line;
964 }
965 }
966
967 return $text;
968}
969
970sub lock_container {
971 my ($vmid, $code, @param) = @_;
972
973 my $filename = $global_vzconf->{lockdir} . "/${vmid}.lck";
974 my $lock;
975 my $res;
976
977 eval {
978
979 my $lockmgr = LockFile::Simple->make(-format => '%f',
980 -autoclean => 1,
981 -max => 30,
982 -delay => 2,
983 -stale => 1,
984 -nfs => 0);
985
986 $lock = $lockmgr->lock($filename) || die "can't lock container $vmid\n";
987
988 $res = &$code(@param);
989
990 };
991 my $err = $@;
992
993 $lock->release() if $lock;
994
995 die $err if $err;
996
997 return $res;
998}
999
1000sub replacepw {
1001 my ($file, $epw) = @_;
1002
1003 my $tmpfile = "$file.$$";
1004
1005 eval {
1006 open (SRC, "<$file") ||
1007 die "unable to open file '$file' - $!";
1008
1009 my $st = File::stat::stat(\*SRC) ||
1010 die "unable to stat file - $!";
1011
1012 open (DST, ">$tmpfile") ||
1013 die "unable to open file '$tmpfile' - $!";
1014
1015 # copy owner and permissions
1016 chmod $st->mode, \*DST;
1017 chown $st->uid, $st->gid, \*DST;
1018
1019 while (defined (my $line = <SRC>)) {
1020 $line =~ s/^root:[^:]*:/root:${epw}:/;
1021 print DST $line;
1022 }
1023 };
1024
1025 my $err = $@;
1026
1027 close (SRC);
1028 close (DST);
1029
1030 if ($err) {
1031 unlink $tmpfile;
1032 } else {
1033 rename $tmpfile, $file;
1034 unlink $tmpfile; # in case rename fails
1035 }
1036}
1037
1038sub set_rootpasswd {
1039 my ($vmid, $opt_rootpasswd) = @_;
1040
1041 my $vmdir = $global_vzconf->{privatedir};
1042 $vmdir =~ s/\$VEID/$vmid/;
1043
1044 my $pwfile = "$vmdir/etc/passwd";
1045
1046 return if ! -f $pwfile;
1047
1048 my $shadow = "$vmdir/etc/shadow";
1049
e761984e
DM
1050 if ($opt_rootpasswd !~ m/^\$/) {
1051 my $time = substr (Digest::SHA1::sha1_base64 (time), 0, 8);
1052 $opt_rootpasswd = crypt(encode("utf8", $opt_rootpasswd), "\$1\$$time\$");
1053 };
1054
339e4159
DM
1055 if (-f $shadow) {
1056 replacepw ($shadow, $opt_rootpasswd);
1057 replacepw ($pwfile, 'x');
1058 } else {
1059 replacepw ($pwfile, $opt_rootpasswd);
1060 }
1061}
1062