6 use POSIX qw
(LONG_MAX
);
9 use PVE
::Tools
qw(extract_param $IPV6RE $IPV4RE);
11 use PVE
::Cluster
qw(cfs_register_file cfs_read_file);
18 use constant SCRIPT_EXT
=> qw
(start stop mount umount premount postumount
);
20 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
21 my $nodename = PVE
::INotify
::nodename
();
22 my $global_vzconf = read_global_vz_config
();
23 my $res_unlimited = LONG_MAX
;
26 my $vmlist = PVE
::Cluster
::get_vmlist
();
28 return $res if !$vmlist || !$vmlist->{ids
};
29 my $ids = $vmlist->{ids
};
31 foreach my $vmid (keys %$ids) {
32 next if !$vmid; # skip VE0
33 my $d = $ids->{$vmid};
34 next if !$d->{node
} || $d->{node
} ne $nodename;
35 next if !$d->{type
} || $d->{type
} ne 'openvz';
36 $res->{$vmid}->{type
} = 'openvz';
42 my ($vmid, $node) = @_;
44 $node = $nodename if !$node;
45 return "nodes/$node/openvz/$vmid.conf";
49 my ($vmid, $node) = @_;
51 my $cfspath = cfs_config_path
($vmid, $node);
52 return "/etc/pve/$cfspath";
58 my $cfspath = cfs_config_path
($vmid);
60 my $conf = PVE
::Cluster
::cfs_read_file
($cfspath);
61 die "container $vmid does not exists\n" if !defined($conf);
67 my ($conf, $vmid) = @_;
69 my $root = get_rootdir
($conf, $vmid);
70 return (-d
"$root/etc" || -d
"$root/proc");
73 # warning: this is slow
77 if (my $fh = new IO
::File
("/proc/vz/vestat", "r")) {
78 while (defined (my $line = <$fh>)) {
79 if ($line =~ m/^\s*(\d+)\s+/) {
92 my ($conf, $vmid) = @_;
94 my $private = $global_vzconf->{privatedir
};
95 if ($conf->{ve_private
} && $conf->{ve_private
}->{value
}) {
96 $private = $conf->{ve_private
}->{value
};
98 $private =~ s/\$VEID/$vmid/;
104 my ($conf, $vmid) = @_;
106 my $root = $global_vzconf->{rootdir
};
107 if ($conf && $conf->{ve_root
} && $conf->{ve_root
}->{value
}) {
108 $root = $conf->{ve_root
}->{value
};
110 $root =~ s/\$VEID/$vmid/;
115 sub read_user_beancounters
{
118 if (my $fh = IO
::File-
>new ("/proc/bc/resources", "r")) {
120 while (defined (my $line = <$fh>)) {
121 if ($line =~ m
|\s
*((\d
+):\s
*)?
([a-z
]+)\s
+(\d
+)\s
+(\d
+)\s
+(\d
+)\s
+(\d
+)\s
+(\d
+)$|) {
122 $vmid = $2 if defined($2);
123 next if !defined($vmid);
124 my ($name, $held, $maxheld, $bar, $lim, $failcnt) = (lc($3), $4, $5, $6, $7, $8);
125 next if $name eq 'dummy';
126 $ubc->{$vmid}->{failcntsum
} += $failcnt;
127 $ubc->{$vmid}->{$name} = {
142 sub read_container_network_usage
{
148 my $netparser = sub {
150 if ($line =~ m/^\s*(.*):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+/) {
151 return if $1 eq 'lo';
157 # fixme: can we get that info directly (with vzctl exec)?
158 my $cmd = ['/usr/sbin/vzctl', 'exec', $vmid, '/bin/cat', '/proc/net/dev'];
159 eval { PVE
::Tools
::run_command
($cmd, outfunc
=> $netparser); };
161 syslog
('err', $err) if $err;
163 return ($recv, $trmt);
166 sub read_container_blkio_stat
{
172 my $filename = "/proc/vz/beancounter/$vmid/blkio.io_service_bytes";
173 if (my $fh = IO
::File-
>new ($filename, "r")) {
175 while (defined (my $line = <$fh>)) {
176 if ($line =~ m/^\S+\s+Read\s+(\d+)$/) {
178 } elsif ($line =~ m/^\S+\s+Write\s+(\d+)$/) {
184 return ($read, $write);
187 my $last_proc_vestat = {};
192 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'openvz' }} : config_list
();
194 my $cpucount = $cpuinfo->{cpus
} || 1;
196 foreach my $vmid (keys %$list) {
197 next if $opt_vmid && ($vmid ne $opt_vmid);
199 my $d = $list->{$vmid};
200 $d->{status
} = 'stopped';
202 my $cfspath = cfs_config_path
($vmid);
203 if (my $conf = PVE
::Cluster
::cfs_read_file
($cfspath)) {
204 $d->{name
} = $conf->{hostname
}->{value
} || "CT$vmid";
205 $d->{name
} =~ s/[\s]//g;
207 $d->{cpus
} = $conf->{cpus
}->{value
} || 1;
208 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
211 $d->{maxdisk
} = int($conf->{diskspace
}->{bar
} * 1024);
216 ($d->{maxmem
}, $d->{maxswap
}) = ovz_config_extract_mem_swap
($conf);
230 if (my $ip = $conf->{ip_address
}->{value
}) {
232 $d->{ip
} = (split(/\s+/, $ip))[0];
237 $d->{status
} = 'mounted' if check_mounted
($conf, $vmid);
240 delete $list->{$vmid};
244 my $maxpages = ($res_unlimited / 4096);
245 my $ubchash = read_user_beancounters
();
246 foreach my $vmid (keys %$ubchash) {
247 my $d = $list->{$vmid};
248 my $ubc = $ubchash->{$vmid};
249 if ($d && defined($d->{status
}) && $ubc) {
250 $d->{failcnt
} = $ubc->{failcntsum
};
251 $d->{mem
} = $ubc->{physpages
}->{held
} * 4096;
252 if ($ubc->{swappages
}->{held
} < $maxpages) {
253 $d->{swap
} = $ubc->{swappages
}->{held
} * 4096
255 $d->{nproc
} = $ubc->{numproc
}->{held
};
259 if (my $fh = IO
::File-
>new ("/proc/vz/vzquota", "r")) {
260 while (defined (my $line = <$fh>)) {
261 if ($line =~ m
|^(\d
+):\s
+\S
+/private/\d
+$|) {
263 my $d = $list->{$vmid};
264 if ($d && defined($d->{status
})) {
266 if ($line =~ m
|^\s
*1k-blocks\s
+(\d
+)\s
+(\d
+)\s
|) {
267 $d->{disk
} = int ($1 * 1024);
268 $d->{maxdisk
} = int ($2 * 1024);
276 # Note: OpenVZ does not use POSIX::_SC_CLK_TCK
279 # see http://wiki.openvz.org/Vestat
280 if (my $fh = new IO
::File
("/proc/vz/vestat", "r")) {
281 while (defined (my $line = <$fh>)) {
282 if ($line =~ m/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
288 my $sum = $8*$cpucount; # uptime in jiffies * cpus = available jiffies
289 my $used = $9; # used time in jiffies
291 my $uptime = int ($ut / $hz);
293 my $d = $list->{$vmid};
294 next if !($d && defined($d->{status
}));
296 $d->{status
} = 'running';
297 $d->{uptime
} = $uptime;
299 if (!defined ($last_proc_vestat->{$vmid}) ||
300 ($last_proc_vestat->{$vmid}->{sum
} > $sum)) {
301 $last_proc_vestat->{$vmid} = { used
=> 0, sum
=> 0, cpu
=> 0 };
304 my $diff = $sum - $last_proc_vestat->{$vmid}->{sum
};
306 if ($diff > 1000) { # don't update too often
307 my $useddiff = $used - $last_proc_vestat->{$vmid}->{used
};
308 my $cpu = (($useddiff/$diff) * $cpucount) / $d->{cpus
};
309 $last_proc_vestat->{$vmid}->{sum
} = $sum;
310 $last_proc_vestat->{$vmid}->{used
} = $used;
311 $last_proc_vestat->{$vmid}->{cpu
} = $d->{cpu
} = $cpu;
313 $d->{cpu
} = $last_proc_vestat->{$vmid}->{cpu
};
320 foreach my $vmid (keys %$list) {
321 my $d = $list->{$vmid};
322 next if !$d || !$d->{status
} || $d->{status
} ne 'running';
323 ($d->{netin
}, $d->{netout
}) = read_container_network_usage
($vmid);
324 ($d->{diskread
}, $d->{diskwrite
}) = read_container_blkio_stat
($vmid);
334 description
=> "Specifies whether a VM will be started during system bootup.",
340 description
=> "The number of CPUs for this container.",
347 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.",
355 description
=> "Amount of RAM for the VM in MB.",
362 description
=> "Amount of SWAP for the VM in MB.",
369 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits.",
376 description
=> "Set quota grace period (seconds).",
383 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.",
389 description
=> "Set a host name for the container.",
396 description
=> "Container description. Only used on the configuration web interface.",
401 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
406 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.",
411 description
=> "Specifies the address the container will be assigned.",
415 type
=> 'string', format
=> 'pve-openvz-netif',
416 description
=> "Specifies network interfaces for the container.",
420 # add JSON properties for create and set function
421 sub json_config_properties
{
424 foreach my $opt (keys %$confdesc) {
425 $prop->{$opt} = $confdesc->{$opt};
431 # read global vz.conf
432 sub read_global_vz_config
{
435 rootdir
=> '/var/lib/vz/root/$VEID', # note '$VEID' is a place holder
436 privatedir
=> '/var/lib/vz/private/$VEID', # note '$VEID' is a place holder
437 dumpdir
=> '/var/lib/vz/dump',
438 lockdir
=> '/var/lib/vz/lock',
441 my $filename = "/etc/vz/vz.conf";
443 return $res if ! -f
$filename;
445 my $data = PVE
::Tools
::file_get_contents
($filename);
447 if ($data =~ m/^\s*VE_PRIVATE=(.*)$/m) {
449 $dir =~ s/^\"(.*)\"/$1/;
450 if ($dir !~ m/\$VEID/) {
451 warn "VE_PRIVATE does not contain '\$VEID' ('$dir')\n";
453 $res->{privatedir
} = $dir;
456 if ($data =~ m/^\s*VE_ROOT=(.*)$/m) {
458 $dir =~ s/^\"(.*)\"/$1/;
459 if ($dir !~ m/\$VEID/) {
460 warn "VE_ROOT does not contain '\$VEID' ('$dir')\n";
462 $res->{rootdir
} = $dir;
465 if ($data =~ m/^\s*DUMPDIR=(.*)$/m) {
467 $dir =~ s/^\"(.*)\"/$1/;
468 $dir =~ s
|/\
$VEID$||;
469 $res->{dumpdir
} = $dir;
471 if ($data =~ m/^\s*LOCKDIR=(.*)$/m) {
473 $dir =~ s/^\"(.*)\"/$1/;
474 $res->{lockdir
} = $dir;
481 my ($data, $vmid) = @_;
484 return $res if !$data;
486 my $host_ifnames = {};
488 my $find_next_hostif_name = sub {
489 for (my $i = 0; $i < 100; $i++) {
490 my $name = "veth${vmid}.$i";
491 if (!$host_ifnames->{$name}) {
492 $host_ifnames->{$name} = 1;
497 die "unable to find free host_ifname"; # should not happen
500 foreach my $iface (split (/;/, $data)) {
502 foreach my $pv (split (/,/, $iface)) {
503 if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(.+)$/) {
504 if ($1 eq 'host_ifname') {
506 $host_ifnames->{$2} = $1;
507 } elsif ($1 eq 'mac_filter') {
508 $d->{$1} = parse_boolean
('mac_filter', $2);
515 $d->{mac
} = PVE
::Tools
::random_ether_addr
() if !$d->{mac
};
516 $d->{host_mac
} = PVE
::Tools
::random_ether_addr
() if !$d->{host_mac
};
517 $d->{raw
} = print_netif
($d);
518 $res->{$d->{ifname
}} = $d;
524 foreach my $iface (keys %$res) {
525 my $d = $res->{$iface};
526 if ($vmid && !$d->{host_ifname
}) {
527 $d->{host_ifname
} = &$find_next_hostif_name($iface);
537 my $res = "ifname=$net->{ifname}";
538 $res .= ",mac=$net->{mac}" if $net->{mac
};
539 $res .= ",host_ifname=$net->{host_ifname}" if $net->{host_ifname
};
540 $res .= ",host_mac=$net->{host_mac}" if $net->{host_mac
};
541 $res .= ",bridge=$net->{bridge}" if $net->{bridge
};
543 if (defined($net->{mac_filter
}) && !$net->{mac_filter
}) {
544 $res .= ",mac_filter=off"; # 'on' is the default
550 PVE
::JSONSchema
::register_format
('pve-openvz-netif', \
&verify_netif
);
552 my ($value, $noerr) = @_;
554 return $value if parse_netif
($value);
556 return undef if $noerr;
558 die "unable to parse --netif value";
561 sub parse_res_num_ignore
{
562 my ($key, $text) = @_;
564 if ($text =~ m/^(\d+|unlimited)(:.*)?$/) {
565 return { bar
=> $1 eq 'unlimited' ?
$res_unlimited : $1 };
571 sub parse_res_num_num
{
572 my ($key, $text) = @_;
574 if ($text =~ m/^(\d+|unlimited)(:(\d+|unlimited))?$/) {
575 my $res = { bar
=> $1 eq 'unlimited' ?
$res_unlimited : $1 };
577 $res->{lim
} = $3 eq 'unlimited' ?
$res_unlimited : $3;
579 $res->{lim
} = $res->{bar
};
587 sub parse_res_bar_limit
{
588 my ($text, $base) = @_;
590 return $res_unlimited if $text eq 'unlimited';
592 if ($text =~ m/^(\d+)([TGMKP])?$/i) {
594 my $mult = $2 ?
lc($2) : '';
597 } elsif ($mult eq 'm') {
598 $val = $val * 1024 * 1024;
599 } elsif ($mult eq 'g') {
600 $val = $val * 1024 * 1024 * 1024;
601 } elsif ($mult eq 't') {
602 $val = $val * 1024 * 1024 * 1024 * 1024;
603 } elsif ($mult eq 'p') {
608 return int($val/$base);
614 sub parse_res_bytes_bytes
{
615 my ($key, $text) = @_;
617 my @a = split(/:/, $text);
618 $a[1] = $a[0] if !defined($a[1]);
620 my $bar = parse_res_bar_limit
($a[0], 1);
621 my $lim = parse_res_bar_limit
($a[1], 1);
623 if (defined($bar) && defined($lim)) {
624 return { bar
=> $bar, lim
=> $lim };
630 sub parse_res_block_block
{
631 my ($key, $text) = @_;
633 my @a = split(/:/, $text);
634 $a[1] = $a[0] if !defined($a[1]);
636 my $bar = parse_res_bar_limit
($a[0], 1024);
637 my $lim = parse_res_bar_limit
($a[1], 1024);
639 if (defined($bar) && defined($lim)) {
640 return { bar
=> $bar, lim
=> $lim };
646 sub parse_res_pages_pages
{
647 my ($key, $text) = @_;
649 my @a = split(/:/, $text);
650 $a[1] = $a[0] if !defined($a[1]);
652 my $bar = parse_res_bar_limit
($a[0], 4096);
653 my $lim = parse_res_bar_limit
($a[1], 4096);
655 if (defined($bar) && defined($lim)) {
656 return { bar
=> $bar, lim
=> $lim };
662 sub parse_res_pages_unlimited
{
663 my ($key, $text) = @_;
665 my @a = split(/:/, $text);
667 my $bar = parse_res_bar_limit
($a[0], 4096);
670 return { bar
=> $bar, lim
=> $res_unlimited };
676 sub parse_res_pages_ignore
{
677 my ($key, $text) = @_;
679 my @a = split(/:/, $text);
681 my $bar = parse_res_bar_limit
($a[0], 4096);
684 return { bar
=> $bar };
690 sub parse_res_ignore_pages
{
691 my ($key, $text) = @_;
693 my @a = split(/:/, $text);
694 $a[1] = $a[0] if !defined($a[1]);
696 my $lim = parse_res_bar_limit
($a[1] , 4096);
699 return { bar
=> 0, lim
=> $lim };
706 my ($key, $text) = @_;
708 return { value
=> 1 } if $text =~ m/^(yes|true|on|1)$/i;
709 return { value
=> 0 } if $text =~ m/^(no|false|off|0)$/i;
715 my ($key, $text) = @_;
717 if ($text =~ m/^(\d+)$/) {
718 return { value
=> int($1) };
724 my $ovz_ressources = {
725 numproc
=> \
&parse_res_num_ignore
,
726 numtcpsock
=> \
&parse_res_num_ignore
,
727 numothersock
=> \
&parse_res_num_ignore
,
728 numfile
=> \
&parse_res_num_ignore
,
729 numflock
=> \
&parse_res_num_num
,
730 numpty
=> \
&parse_res_num_ignore
,
731 numsiginfo
=> \
&parse_res_num_ignore
,
732 numiptent
=> \
&parse_res_num_ignore
,
734 vmguarpages
=> \
&parse_res_pages_unlimited
,
735 oomguarpages
=> \
&parse_res_pages_unlimited
,
736 lockedpages
=> \
&parse_res_pages_ignore
,
737 privvmpages
=> \
&parse_res_pages_pages
,
738 shmpages
=> \
&parse_res_pages_ignore
,
739 physpages
=> \
&parse_res_pages_pages
,
740 swappages
=> \
&parse_res_ignore_pages
,
742 kmemsize
=> \
&parse_res_bytes_bytes
,
743 tcpsndbuf
=> \
&parse_res_bytes_bytes
,
744 tcprcvbuf
=> \
&parse_res_bytes_bytes
,
745 othersockbuf
=> \
&parse_res_bytes_bytes
,
746 dgramrcvbuf
=> \
&parse_res_bytes_bytes
,
747 dcachesize
=> \
&parse_res_bytes_bytes
,
749 disk_quota
=> \
&parse_boolean
,
750 diskspace
=> \
&parse_res_block_block
,
751 diskinodes
=> \
&parse_res_num_num
,
752 quotatime
=> \
&parse_integer
,
753 quotaugidlimit
=> \
&parse_integer
,
755 cpuunits
=> \
&parse_integer
,
756 cpulimit
=> \
&parse_integer
,
757 cpus
=> \
&parse_integer
,
760 iptables
=> 'string',
762 ip_address
=> 'string',
764 hostname
=> 'string',
765 nameserver
=> 'string',
766 searchdomain
=> 'string',
769 description
=> 'string',
770 onboot
=> \
&parse_boolean
,
771 initlog
=> \
&parse_boolean
,
772 bootorder
=> \
&parse_integer
,
773 ostemplate
=> 'string',
775 ve_private
=> 'string',
776 disabled
=> \
&parse_boolean
,
777 origin_sample
=> 'string',
778 noatime
=> \
&parse_boolean
,
779 capability
=> 'string',
780 devnodes
=> 'string',
783 features
=> 'string',
784 ioprio
=> \
&parse_integer
,
788 sub parse_ovz_config
{
789 my ($filename, $raw) = @_;
791 return undef if !defined($raw);
794 digest
=> Digest
::SHA
::sha1_hex
($raw),
797 $filename =~ m
|/openvz/(\d
+)\
.conf
$|
798 || die "got strange filename '$filename'";
802 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
805 next if $line =~ m/^\#/;
806 next if $line =~ m/^\s*$/;
808 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
812 my $parser = $ovz_ressources->{$name};
813 if (!$parser || !ref($parser)) {
814 $data->{$name}->{value
} = $text;
817 if (my $res = &$parser($name, $text)) {
818 $data->{$name} = $res;
823 die "unable to parse config line: $line\n";
829 cfs_register_file
('/openvz/', \
&parse_ovz_config
);
831 sub format_res_value
{
832 my ($key, $value) = @_;
834 return 'unlimited' if $value == $res_unlimited;
836 return 0 if $value == 0;
838 if ($key =~ m/pages$/) {
839 my $bytes = $value * 4096;
840 my $mb = int ($bytes / (1024 * 1024));
841 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
842 } elsif ($key =~ m/space$/) {
843 my $bytes = $value * 1024;
844 my $gb = int ($bytes / (1024 * 1024 * 1024));
845 return "${gb}G" if $gb * 1024 * 1024 * 1024 == $bytes;
846 my $mb = int ($bytes / (1024 * 1024));
847 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
848 } elsif ($key =~ m/size$/) {
850 my $mb = int ($bytes / (1024 * 1024));
851 return "${mb}M" if $mb * 1024 * 1024 == $bytes;
857 sub format_res_bar_lim
{
858 my ($key, $data) = @_;
860 if (defined($data->{lim
}) && ($data->{lim
} ne $data->{bar
})) {
861 return format_res_value
($key, $data->{bar
}) . ":" . format_res_value
($key, $data->{lim
});
863 return format_res_value
($key, $data->{bar
});
867 sub create_config_line
{
868 my ($key, $data) = @_;
872 if (defined($data->{value
})) {
873 if ($confdesc->{$key} && $confdesc->{$key}->{type
} eq 'boolean') {
874 my $txt = $data->{value
} ?
'yes' : 'no';
875 $text .= uc($key) . "=\"$txt\"\n";
877 $text .= uc($key) . "=\"$data->{value
}\"\n";
879 } elsif (defined($data->{bar})) {
880 my $tmp = format_res_bar_lim($key, $data);
881 $text .= uc($key) . "=\"$tmp\"\n";
885 sub ovz_config_extract_mem_swap
{
886 my ($veconf, $unit) = @_;
890 my ($mem, $swap) = (int((512*1024*1024 + $unit - 1)/$unit), 0);
892 my $maxpages = ($res_unlimited / 4096);
894 if ($veconf->{swappages
}) {
895 if ($veconf->{physpages
} && $veconf->{physpages
}->{lim
} &&
896 ($veconf->{physpages
}->{lim
} < $maxpages)) {
897 $mem = int(($veconf->{physpages
}->{lim
} * 4096 + $unit - 1) / $unit);
899 if ($veconf->{swappages
}->{lim
} && ($veconf->{swappages
}->{lim
} < $maxpages)) {
900 $swap = int (($veconf->{swappages
}->{lim
} * 4096 + $unit - 1) / $unit);
903 if ($veconf->{vmguarpages
} && $veconf->{vmguarpages
}->{bar
} &&
904 ($veconf->{vmguarpages
}->{bar
} < $maxpages)) {
905 $mem = int(($veconf->{vmguarpages
}->{bar
} * 4096 + $unit - 1) / $unit);
909 return ($mem, $swap);
912 sub update_ovz_config
{
913 my ($vmid, $veconf, $param) = @_;
917 # test if barrier or limit changed
918 my $push_bl_changes = sub {
919 my ($name, $bar, $lim) = @_;
920 my $old = format_res_bar_lim
($name, $veconf->{$name})
921 if $veconf->{$name} && defined($veconf->{$name}->{bar
});
922 my $new = format_res_bar_lim
($name, { bar
=> $bar, lim
=> $lim });
923 if (!$old || ($old ne $new)) {
924 $veconf->{$name}->{bar
} = $bar;
925 $veconf->{$name}->{lim
} = $lim;
926 push @$changes, "--$name", $new;
930 my ($mem, $swap) = ovz_config_extract_mem_swap
($veconf, 1024*1024);
931 my $disk = ($veconf->{diskspace
}->{bar
} || $res_unlimited) / (1024*1024);
932 my $cpuunits = $veconf->{cpuunits
}->{value
} || 1000;
933 my $quotatime = $veconf->{quotatime
}->{value
} || 0;
934 my $quotaugidlimit = $veconf->{quotaugidlimit
}->{value
} || 0;
935 my $cpus = $veconf->{cpus
}->{value
} || 1;
937 if ($param->{memory
}) {
938 $mem = $param->{memory
};
941 if (defined ($param->{swap
})) {
942 $swap = $param->{swap
};
945 if ($param->{disk
}) {
946 $disk = $param->{disk
};
949 if ($param->{cpuunits
}) {
950 $cpuunits = $param->{cpuunits
};
953 if (defined($param->{quotatime
})) {
954 $quotatime = $param->{quotatime
};
957 if (defined($param->{quotaugidlimit
})) {
958 $quotaugidlimit = $param->{quotaugidlimit
};
961 if ($param->{cpus
}) {
962 $cpus = $param->{cpus
};
965 # memory related parameter
967 &$push_bl_changes('vmguarpages', 0, $res_unlimited);
968 &$push_bl_changes('oomguarpages', 0, $res_unlimited);
969 &$push_bl_changes('privvmpages', $res_unlimited, $res_unlimited);
972 my $lockedpages = int($mem*1024/8);
973 &$push_bl_changes('lockedpages', $lockedpages, undef);
975 my $kmemsize = int($mem/2);
976 &$push_bl_changes('kmemsize', int($kmemsize/1.1
)*1024*1024, $kmemsize*1024*1024);
978 my $dcachesize = int($mem/4);
979 &$push_bl_changes('dcachesize', int($dcachesize/1.1
)*1024*1024, $dcachesize*1024*1024);
981 my $physpages = int($mem*1024/4);
982 &$push_bl_changes('physpages', 0, $physpages);
984 my $swappages = int($swap*1024/4);
985 &$push_bl_changes('swappages', 0, $swappages);
988 # disk quota parameters
989 if (!$disk || ($disk * 1.1) >= ($res_unlimited / (1024 * 1024))) {
990 &$push_bl_changes('diskspace', $res_unlimited, $res_unlimited);
991 &$push_bl_changes('diskinodes', $res_unlimited, $res_unlimited);
993 my $diskspace = int ($disk * 1024 * 1024);
994 my $diskspace_lim = int ($diskspace * 1.1);
995 &$push_bl_changes('diskspace', $diskspace, $diskspace_lim);
996 my $diskinodes = int ($disk * 200000);
997 my $diskinodes_lim = int ($disk * 220000);
998 &$push_bl_changes('diskinodes', $diskinodes, $diskinodes_lim);
1000 if ($veconf->{'quotatime'}->{value
} != $quotatime) {
1001 $veconf->{'quotatime'}->{value
} = $quotatime;
1002 push @$changes, '--quotatime', "$quotatime";
1005 if ($veconf->{'quotaugidlimit'}->{value
} != $quotaugidlimit) {
1006 $veconf->{'quotaugidlimit'}->{value
} = $quotaugidlimit;
1007 push @$changes, '--quotaugidlimit', "$quotaugidlimit";
1012 if ($veconf->{'cpuunits'}->{value
} != $cpuunits) {
1013 $veconf->{'cpuunits'}->{value
} = $cpuunits;
1014 push @$changes, '--cpuunits', "$cpuunits";
1017 if ($veconf->{'cpus'}->{value
} != $cpus) {
1018 $veconf->{'cpus'}->{value
} = $cpus;
1019 push @$changes, '--cpus', "$cpus";
1022 my $cond_set_boolean = sub {
1025 return if !defined($param->{$name});
1027 my $newvalue = $param->{$name} ?
1 : 0;
1028 my $oldvalue = $veconf->{$name}->{value
};
1029 if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
1030 $veconf->{$name}->{value
} = $newvalue;
1031 push @$changes, "--$name", $newvalue ?
'yes' : 'no';
1035 my $cond_set_value = sub {
1036 my ($name, $newvalue) = @_;
1038 $newvalue = defined($newvalue) ?
$newvalue : $param->{$name};
1039 return if !defined($newvalue);
1041 my $oldvalue = $veconf->{$name}->{value
};
1042 if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
1043 $veconf->{$name}->{value
} = $newvalue;
1044 push @$changes, "--$name", $newvalue;
1048 &$cond_set_boolean('onboot');
1050 &$cond_set_value('hostname');
1052 &$cond_set_value('searchdomain');
1054 if ($param->{'description'}) {
1055 &$cond_set_value('description', PVE
::Tools
::encode_text
($param->{'description'}));
1058 if (defined($param->{ip_address
})) {
1060 if (defined($veconf->{'ip_address'}) && $veconf->{'ip_address'}->{value
}) {
1061 foreach my $ip (split (/\s+/, $veconf->{ip_address
}->{value
})) {
1066 foreach my $ip (PVE
::Tools
::split_list
($param->{'ip_address'})) {
1067 next if $ip !~ m!^(?:$IPV6RE|$IPV4RE)(?:/\d+)?$!;
1068 $newhash->{$ip} = 1;
1069 if (!$iphash->{$ip}) {
1070 push @$changes, '--ipadd', $ip;
1071 $iphash->{$ip} = 1; # only add once
1074 foreach my $ip (keys %$iphash) {
1075 if (!$newhash->{$ip}) {
1076 push @$changes, '--ipdel', $ip;
1079 $veconf->{'ip_address'}->{value
} = join(' ', keys %$iphash);
1082 if (defined($param->{netif
})) {
1084 if (defined ($veconf->{netif
}) && $veconf->{netif
}->{value
}) {
1085 $ifaces = parse_netif
($veconf->{netif
}->{value
}, $vmid);
1087 my $newif = parse_netif
($param->{netif
}, $vmid);
1089 foreach my $ifname (sort keys %$ifaces) {
1090 if (!$newif->{$ifname}) {
1091 push @$changes, '--netif_del', $ifname;
1096 foreach my $ifname (sort keys %$newif) {
1097 $newvalue .= ';' if $newvalue;
1099 $newvalue .= print_netif
($newif->{$ifname});
1101 my $ifadd = $ifname;
1102 $ifadd .= $newif->{$ifname}->{mac
} ?
",$newif->{$ifname}->{mac}" : ',';
1103 $ifadd .= $newif->{$ifname}->{host_ifname
} ?
",$newif->{$ifname}->{host_ifname}" : ',';
1104 $ifadd .= $newif->{$ifname}->{host_mac
} ?
",$newif->{$ifname}->{host_mac}" : ',';
1105 $ifadd .= $newif->{$ifname}->{bridge
} ?
",$newif->{$ifname}->{bridge}" : '';
1107 # not possible with current vzctl
1108 #$ifadd .= $newif->{$ifname}->{mac_filter} ? ",$newif->{$ifname}->{mac_filter}" : '';
1110 if (!$ifaces->{$ifname} || ($ifaces->{$ifname}->{raw
} ne $newif->{$ifname}->{raw
})) {
1111 push @$changes, '--netif_add', $ifadd;
1114 $veconf->{netif
}->{value
} = $newvalue;
1117 if (defined($param->{'nameserver'})) {
1121 foreach my $ns (PVE
::Tools
::split_list
($param->{'nameserver'})) {
1122 if (!$nshash->{$ns}) {
1123 push @$changes, '--nameserver', $ns;
1125 $newvalue .= $newvalue ?
" $ns" : $ns;
1128 $veconf->{'nameserver'}->{value
} = $newvalue if $newvalue;
1131 # foreach my $nv (@$changes) { print "CHANGE: $nv\n"; }
1136 sub generate_raw_config
{
1137 my ($raw, $conf) = @_;
1143 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
1146 if ($line =~ m/^\#/ || $line =~ m/^\s*$/) {
1151 if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
1153 if ($conf->{$name}) {
1154 $found->{$name} = 1;
1155 if (my $line = create_config_line
($name, $conf->{$name})) {
1162 foreach my $key (keys %$conf) {
1163 next if $found->{$key};
1164 next if $key eq 'digest';
1165 if (my $line = create_config_line
($key, $conf->{$key})) {
1173 sub create_lock_manager
{
1176 return LockFile
::Simple-
>make(-format
=> '%f',
1178 -max
=> defined($max) ?
$max : 60,
1184 sub lock_container
{
1185 my ($vmid, $max, $code, @param) = @_;
1187 my $filename = $global_vzconf->{lockdir
} . "/${vmid}.lck";
1193 my $lockmgr = create_lock_manager
($max);
1195 $lock = $lockmgr->lock($filename) || die "can't lock container $vmid\n";
1197 $res = &$code(@param);
1201 $lock->release() if $lock;
1209 my ($file, $epw) = @_;
1211 my $tmpfile = "$file.$$";
1214 open (SRC
, "<$file") ||
1215 die "unable to open file '$file' - $!";
1217 my $st = File
::stat::stat(\
*SRC
) ||
1218 die "unable to stat file - $!";
1220 open (DST
, ">$tmpfile") ||
1221 die "unable to open file '$tmpfile' - $!";
1223 # copy owner and permissions
1224 chmod $st->mode, \
*DST
;
1225 chown $st->uid, $st->gid, \
*DST
;
1227 while (defined (my $line = <SRC
>)) {
1228 $line =~ s/^root:[^:]*:/root:${epw}:/;
1241 rename $tmpfile, $file;
1242 unlink $tmpfile; # in case rename fails
1246 sub set_rootpasswd
{
1247 my ($privatedir, $opt_rootpasswd) = @_;
1249 my $pwfile = "$privatedir/etc/passwd";
1251 return if ! -f
$pwfile;
1253 my $shadow = "$privatedir/etc/shadow";
1255 if ($opt_rootpasswd !~ m/^\$/) {
1256 my $time = substr (Digest
::SHA
::sha1_base64
(time), 0, 8);
1257 $opt_rootpasswd = crypt(encode
("utf8", $opt_rootpasswd), "\$1\$$time\$");
1261 replacepw
($shadow, $opt_rootpasswd);
1262 replacepw
($pwfile, 'x');
1264 replacepw
($pwfile, $opt_rootpasswd);