]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/LXC.pm
10 use PVE
:: Cluster
qw(cfs_register_file cfs_read_file) ;
14 use PVE
:: JSONSchema
qw(get_standard_option) ;
15 use PVE
:: Tools
qw( $IPV6RE $IPV4RE ) ;
19 cfs_register_file
( '/lxc/' , \
& parse_lxc_config
, \
& write_lxc_config
);
21 PVE
:: JSONSchema
:: register_format
( 'pve-lxc-network' , \
& verify_lxc_network
);
22 sub verify_lxc_network
{
23 my ( $value, $noerr ) = @_ ;
25 return $value if parse_lxc_network
( $value );
27 return undef if $noerr ;
29 die "unable to parse network setting \n " ;
32 my $nodename = PVE
:: INotify
:: nodename
();
35 my ( $name, $value ) = @_ ;
37 if ( $value =~ m/^(\d+)(b|k|m|g)?$/i ) {
38 my ( $res, $unit ) = ( $1, lc ( $2 || 'b' ));
40 return $res if $unit eq 'b' ;
41 return $res*1024 if $unit eq 'k' ;
42 return $res*1024*1024 if $unit eq 'm' ;
43 return $res*1024*1024*1024 if $unit eq 'g' ;
49 my $valid_lxc_keys = {
50 'lxc.arch' => 'i386|x86|i686|x86_64|amd64' ,
58 'lxc.cgroup.memory.limit_in_bytes' => \
& parse_lxc_size
,
59 'lxc.cgroup.memory.memsw.limit_in_bytes' => \
& parse_lxc_size
,
60 'lxc.cgroup.cpu.cfs_period_us' => '\d+' ,
61 'lxc.cgroup.cpu.cfs_quota_us' => '\d+' ,
62 'lxc.cgroup.cpu.shares' => '\d+' ,
66 'lxc.mount.entry' => 1 ,
67 'lxc.mount.auto' => 1 ,
72 'lxc.haltsignal' => 1 ,
73 'lxc.rebootsignal' => 1 ,
74 'lxc.stopsignal' => 1 ,
77 'lxc.console.logfile' => 1 ,
83 'lxc.aa_profile' => 1 ,
84 'lxc.aa_allow_incomplete' => 1 ,
85 'lxc.se_context' => 1 ,
88 'lxc.environment' => 1 ,
89 'lxc.cgroup.devices.deny' => 1 ,
92 'lxc.start.auto' => 1 ,
93 'lxc.start.delay' => 1 ,
94 'lxc.start.order' => 1 ,
98 'lxc.hook.pre-start' => 1 ,
99 'lxc.hook.pre-mount' => 1 ,
100 'lxc.hook.mount' => 1 ,
101 'lxc.hook.autodev' => 1 ,
102 'lxc.hook.start' => 1 ,
103 'lxc.hook.post-stop' => 1 ,
104 'lxc.hook.clone' => 1 ,
107 'pve.nameserver' => sub {
108 my ( $name, $value ) = @_ ;
109 return verify_nameserver_list
( $value );
111 'pve.searchdomain' => sub {
112 my ( $name, $value ) = @_ ;
113 return verify_searchdomain_list
( $value );
115 'pve.onboot' => '(0|1)' ,
116 'pve.startup' => sub {
117 my ( $name, $value ) = @_ ;
118 return PVE
:: JSONSchema
:: pve_verify_startup_order
( $value );
121 'pve.disksize' => '\d+(\.\d+)?' ,
123 my ( $name, $value ) = @_ ;
124 PVE
:: Storage
:: parse_volume_id
( $value );
129 my $valid_lxc_network_keys = {
132 name
=> 1 , # ifname inside container
133 'veth.pair' => 1 , # ifname at host (eth${vmid}.X)
137 my $valid_pve_network_keys = {
147 my $lxc_array_configs = {
152 'lxc.cgroup.devices.deny' => 1 ,
155 sub write_lxc_config
{
156 my ( $filename, $data ) = @_ ;
160 return $raw if ! $data ;
162 my $done_hash = { digest
=> 1 };
164 my $dump_entry = sub {
166 my $value = $data ->{ $k };
167 return if ! defined ( $value );
168 return if $done_hash ->{ $k };
169 $done_hash ->{ $k } = 1 ;
171 die "got unexpected reference for ' $k '"
172 if ! $lxc_array_configs ->{ $k };
173 foreach my $v ( @$value ) {
177 $raw .= " $k = $value\n " ;
181 # Note: Order is important! Include defaults first, so that we
182 # can overwrite them later.
183 & $dump_entry ( 'lxc.include' );
185 foreach my $k ( sort keys %$data ) {
186 next if $k !~ m/^lxc\./ ;
190 foreach my $k ( sort keys %$data ) {
191 next if $k !~ m/^pve\./ ;
195 my $network_count = 0 ;
196 foreach my $k ( sort keys %$data ) {
197 next if $k !~ m/^net\d+$/ ;
198 $done_hash ->{ $k } = 1 ;
199 my $net = $data ->{ $k };
201 $raw .= "lxc.network.type = $net ->{type} \n " ;
202 foreach my $subkey ( sort keys %$net ) {
203 next if $subkey eq 'type' ;
204 if ( $valid_lxc_network_keys ->{ $subkey }) {
205 $raw .= "lxc.network. $subkey = $net ->{ $subkey } \n " ;
206 } elsif ( $valid_pve_network_keys ->{ $subkey }) {
207 $raw .= "pve.network. $subkey = $net ->{ $subkey } \n " ;
209 die "found invalid network key ' $subkey '" ;
214 if (! $network_count ) {
215 $raw .= "lxc.network.type = empty \n " ;
218 foreach my $k ( sort keys %$data ) {
219 next if $done_hash ->{ $k };
220 die "found un-written value in config - implement this!" ;
226 sub parse_lxc_option
{
227 my ( $name, $value ) = @_ ;
229 my $parser = $valid_lxc_keys ->{ $name };
231 die "invalid key ' $name ' \n " if ! defined ( $parser );
233 if ( $parser eq '1' ) {
235 } elsif ( ref ( $parser )) {
236 my $res = & $parser ( $name, $value );
237 return $res if defined ( $res );
240 return $value if $value =~ m/^$parser$/ ;
243 die "unable to parse value ' $value ' for option ' $name ' \n " ;
246 sub parse_lxc_config
{
247 my ( $filename, $raw ) = @_ ;
249 return undef if ! defined ( $raw );
252 digest
=> Digest
:: SHA
:: sha1_hex
( $raw ),
255 $filename =~ m
| /lxc/ ( \d
+)/ config
$|
256 || die "got strange filename ' $filename '" ;
260 my $network_counter = 0 ;
261 my $network_list = [];
262 my $host_ifnames = {};
264 my $find_next_hostif_name = sub {
265 for ( my $i = 0 ; $i < 10 ; $i++ ) {
266 my $name = "veth${vmid}. $i " ;
267 if (! $host_ifnames ->{ $name }) {
268 $host_ifnames ->{ $name } = 1 ;
273 die "unable to find free host_ifname" ; # should not happen
276 my $push_network = sub {
279 push @{ $network_list }, $netconf ;
281 if ( my $netname = $netconf ->{ 'veth.pair' }) {
282 if ( $netname =~ m/^veth(\d+).(\d)$/ ) {
283 die "wrong vmid for network interface pair \n " if $1 != $vmid ;
284 my $host_ifnames ->{ $netname } = 1 ;
286 die "wrong network interface pair \n " ;
293 while ( $raw && $raw =~ s/^(.*?)(\n|$)// ) {
296 next if $line =~ m/^\#/ ;
297 next if $line =~ m/^\s*$/ ;
299 if ( $line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
300 my ( $subkey, $value ) = ( $1, $2 );
301 if ( $subkey eq 'type' ) {
302 & $push_network ( $network );
303 $network = { type
=> $value };
304 } elsif ( $valid_lxc_network_keys ->{ $subkey }) {
305 $network ->{ $subkey } = $value ;
307 die "unable to parse config line: $line\n " ;
311 if ( $line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
312 my ( $subkey, $value ) = ( $1, $2 );
313 if ( $valid_pve_network_keys ->{ $subkey }) {
314 $network ->{ $subkey } = $value ;
316 die "unable to parse config line: $line\n " ;
320 if ( $line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/ ) {
321 my ( $name, $value ) = ( $1, $2 );
322 $data ->{ $name } = $value ;
325 if ( $line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/ ) {
326 my ( $name, $value ) = ( $1, $2 );
328 if ( $lxc_array_configs ->{ $name }) {
329 $data ->{ $name } = [] if ! defined ( $data ->{ $name });
330 push @{ $data ->{ $name }}, parse_lxc_option
( $name, $value );
332 die "multiple definitions for $name\n " if defined ( $data ->{ $name });
333 $data ->{ $name } = parse_lxc_option
( $name, $value );
339 die "unable to parse config line: $line\n " ;
342 & $push_network ( $network );
344 foreach my $net (@{ $network_list }) {
345 next if $net ->{ type
} eq 'empty' ; # skip
346 $net ->{ 'veth.pair' } = & $find_next_hostif_name () if ! $net ->{ 'veth.pair' };
347 $net ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $net ->{ hwaddr
};
348 die "unsupported network type ' $net ->{type}' \n " if $net ->{ type
} ne 'veth' ;
350 if ( $net ->{ 'veth.pair' } =~ m/^veth\d+.(\d+)$/ ) {
351 $data ->{ "net $1 " } = $net ;
359 my $vmlist = PVE
:: Cluster
:: get_vmlist
();
361 return $res if ! $vmlist || ! $vmlist ->{ ids
};
362 my $ids = $vmlist ->{ ids
};
364 foreach my $vmid ( keys %$ids ) {
365 next if ! $vmid ; # skip CT0
366 my $d = $ids ->{ $vmid };
367 next if ! $d ->{ node
} || $d ->{ node
} ne $nodename ;
368 next if ! $d ->{ type
} || $d ->{ type
} ne 'lxc' ;
369 $res ->{ $vmid }->{ type
} = 'lxc' ;
374 sub cfs_config_path
{
375 my ( $vmid, $node ) = @_ ;
377 $node = $nodename if ! $node ;
378 return "nodes/ $node/lxc/$vmid/config " ;
382 my ( $vmid, $node ) = @_ ;
384 my $cfspath = cfs_config_path
( $vmid, $node );
385 return "/etc/pve/ $cfspath " ;
391 my $cfspath = cfs_config_path
( $vmid );
393 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath );
394 die "container $vmid does not exists \n " if ! defined ( $conf );
400 my ( $vmid, $conf ) = @_ ;
402 my $dir = "/etc/pve/nodes/ $nodename/lxc " ;
406 mkdir ( $dir ) || die "unable to create container configuration directory - $!\n " ;
408 write_config
( $vmid, $conf );
414 my $dir = "/etc/pve/nodes/ $nodename/lxc/$vmid " ;
415 File
:: Path
:: rmtree
( $dir );
419 my ( $vmid, $conf ) = @_ ;
421 my $cfspath = cfs_config_path
( $vmid );
423 PVE
:: Cluster
:: cfs_write_file
( $cfspath, $conf );
427 sub write_temp_config
{
428 my ( $vmid, $conf ) = @_ ;
431 my $filename = "/tmp/temp-lxc-conf- $vmid - $$ - $tempcounter .conf" ;
433 my $raw = write_lxc_config
( $filename, $conf );
435 PVE
:: Tools
:: file_set_contents
( $filename, $raw );
440 # flock: we use one file handle per process, so lock file
441 # can be called multiple times and succeeds for the same process.
443 my $lock_handles = {};
444 my $lockdir = "/run/lock/lxc" ;
449 return " $lockdir/pve -config-{ $vmid }.lock" ;
453 my ( $vmid, $timeout ) = @_ ;
455 $timeout = 10 if ! $timeout ;
458 my $filename = lock_filename
( $vmid );
460 mkdir $lockdir if !- d
$lockdir ;
462 my $lock_func = sub {
463 if (! $lock_handles ->{ $$ }->{ $filename }) {
464 my $fh = new IO
:: File
( ">> $filename " ) ||
465 die "can't open file - $!\n " ;
466 $lock_handles ->{ $$ }->{ $filename } = { fh
=> $fh, refcount
=> 0 };
469 if (! flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode | LOCK_NB
)) {
470 print STDERR
"trying to aquire lock..." ;
473 $success = flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode );
474 # try again on EINTR (see bug #273)
475 if ( $success || ( $! != EINTR
)) {
480 print STDERR
" failed \n " ;
481 die "can't aquire lock - $!\n " ;
484 $lock_handles ->{ $$ }->{ $filename }->{ refcount
}++;
486 print STDERR
" OK \n " ;
490 eval { PVE
:: Tools
:: run_with_timeout
( $timeout, $lock_func ); };
493 die "can't lock file ' $filename ' - $err " ;
500 my $filename = lock_filename
( $vmid );
502 if ( my $fh = $lock_handles ->{ $$ }->{ $filename }->{ fh
}) {
503 my $refcount = -- $lock_handles ->{ $$ }->{ $filename }->{ refcount
};
504 if ( $refcount <= 0 ) {
505 $lock_handles ->{ $$ }->{ $filename } = undef ;
512 my ( $vmid, $timeout, $code, @param ) = @_ ;
516 lock_aquire
( $vmid, $timeout );
517 eval { $res = & $code ( @param ) };
530 description
=> "Specifies whether a VM will be started during system bootup." ,
533 startup
=> get_standard_option
( 'pve-startup-order' ),
537 description
=> "Limit of CPU usage. Note if the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit." ,
545 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\n NOTE: You can disable fair-scheduler configuration by setting this to 0." ,
553 description
=> "Amount of RAM for the VM in MB." ,
560 description
=> "Amount of SWAP for the VM in MB." ,
567 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
573 description
=> "Set a host name for the container." ,
580 description
=> "Container description. Only used on the configuration web interface." ,
585 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
590 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." ,
594 my $MAX_LXC_NETWORKS = 10 ;
595 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
596 $confdesc ->{ "net $i " } = {
598 type
=> 'string' , format
=> 'pve-lxc-network' ,
599 description
=> "Specifies network interfaces for the container. \n\n " .
600 "The string should have the follow format: \n\n " .
601 "-net<[0-9]> bridge=<vmbr<Nummber>>[,hwaddr=<MAC>] \n " .
602 "[,mtu=<Number>][,name=<String>][,ip=<IPv4Format/CIDR>] \n " .
603 ",ip6=<IPv6Format/CIDR>][,gw=<GatwayIPv4>] \n " .
604 ",gw6=<GatwayIPv6>][,firewall=<[1|0]>][,tag=<VlanNo>]" ,
611 return defined ( $confdesc ->{ $name });
614 # add JSON properties for create and set function
615 sub json_config_properties
{
618 foreach my $opt ( keys %$confdesc ) {
619 $prop ->{ $opt } = $confdesc ->{ $opt };
625 # container status helpers
627 sub list_active_containers
{
629 my $filename = "/proc/net/unix" ;
631 # similar test is used by lcxcontainers.c: list_active_containers
634 my $fh = IO
:: File-
> new ( $filename, "r" );
637 while ( defined ( my $line = < $fh >)) {
638 if ( $line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/ ) {
640 if ( $path =~ m!^@/etc/pve/lxc/(\d+)/command$! ) {
651 # warning: this is slow
655 my $active_hash = list_active_containers
();
657 return 1 if defined ( $active_hash ->{ $vmid });
662 sub get_container_disk_usage
{
665 my $cmd = [ 'lxc-attach' , '-n' , $vmid, '--' , 'df' , '-P' , '-B' , '1' , '/' ];
675 if ( my ( $fsid, $total, $used, $avail ) = $line =~
676 m/^(\S+.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s.*$/ ) {
684 eval { PVE
:: Tools
:: run_command
( $cmd, timeout
=> 1 , outfunc
=> $parser ); };
693 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'lxc' }} : config_list
();
695 my $active_hash = list_active_containers
();
697 foreach my $vmid ( keys %$list ) {
698 my $d = $list ->{ $vmid };
700 my $running = defined ( $active_hash ->{ $vmid });
702 $d ->{ status
} = $running ?
'running' : 'stopped' ;
704 my $cfspath = cfs_config_path
( $vmid );
705 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath ) || {};
707 $d ->{ name
} = $conf ->{ 'lxc.utsname' } || "CT $vmid " ;
708 $d ->{ name
} =~ s/[\s]//g ;
712 my $cfs_period_us = $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
713 my $cfs_quota_us = $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
715 if ( $cfs_period_us && $cfs_quota_us ) {
716 $d ->{ cpus
} = int ( $cfs_quota_us/$cfs_period_us );
720 $d ->{ maxdisk
} = defined ( $conf ->{ 'pve.disksize' }) ?
721 int ( $conf ->{ 'pve.disksize' }* 1024 * 1024 )* 1024 : 1024 * 1024 * 1024 * 1024 * 1024 ;
723 if ( my $private = $conf ->{ 'lxc.rootfs' }) {
724 if ( $private =~ m!^/! ) {
725 my $res = PVE
:: Tools
:: df
( $private, 2 );
726 $d ->{ disk
} = $res ->{ used
};
727 $d ->{ maxdisk
} = $res ->{ total
};
729 if ( $private =~ m!^(?:loop|nbd):(?:\S+)$! ) {
730 my $res = get_container_disk_usage
( $vmid );
731 $d ->{ disk
} = $res ->{ used
};
732 $d ->{ maxdisk
} = $res ->{ total
};
739 $d ->{ maxmem
} = ( $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }|| 0 ) +
740 ( $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }|| 0 );
752 foreach my $vmid ( keys %$list ) {
753 my $d = $list ->{ $vmid };
754 next if $d ->{ status
} ne 'running' ;
756 $d ->{ uptime
} = 100 ; # fixme:
758 $d ->{ mem
} = read_cgroup_value
( 'memory' , $vmid, 'memory.usage_in_bytes' );
759 $d ->{ swap
} = read_cgroup_value
( 'memory' , $vmid, 'memory.memsw.usage_in_bytes' ) - $d ->{ mem
};
761 my $blkio_bytes = read_cgroup_value
( 'blkio' , $vmid, 'blkio.throttle.io_service_bytes' , 1 );
762 my @bytes = split /\n/ , $blkio_bytes ;
763 foreach my $byte ( @bytes ) {
764 my ( $key, $value ) = $byte =~ /(Read|Write)\s+(\d+)/ ;
765 $d ->{ diskread
} = $2 if $key eq 'Read' ;
766 $d ->{ diskwrite
} = $2 if $key eq 'Write' ;
774 sub print_lxc_network
{
777 die "no network bridge defined \n " if ! $net ->{ bridge
};
779 my $res = "bridge= $net ->{bridge}" ;
781 foreach my $k ( qw(hwaddr mtu name ip gw ip6 gw6 firewall tag) ) {
782 next if ! defined ( $net ->{ $k });
783 $res .= ", $k = $net ->{ $k }" ;
789 sub parse_lxc_network
{
794 return $res if ! $data ;
796 foreach my $pv ( split ( /,/ , $data )) {
797 if ( $pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/ ) {
804 $res ->{ type
} = 'veth' ;
805 $res ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $res ->{ mac
};
810 sub read_cgroup_value
{
811 my ( $group, $vmid, $name, $full ) = @_ ;
813 my $path = "/sys/fs/cgroup/ $group/lxc/$vmid/$name " ;
815 return PVE
:: Tools
:: file_get_contents
( $path ) if $full ;
817 return PVE
:: Tools
:: file_read_firstline
( $path );
820 sub write_cgroup_value
{
821 my ( $group, $vmid, $name, $value ) = @_ ;
823 my $path = "/sys/fs/cgroup/ $group/lxc/$vmid/$name " ;
824 PVE
:: ProcFSTools
:: write_proc_entry
( $path, $value ) if - e
$path ;
828 sub find_lxc_console_pids
{
832 PVE
:: Tools
:: dir_glob_foreach
( '/proc' , '\d+' , sub {
835 my $cmdline = PVE
:: Tools
:: file_read_firstline
( "/proc/ $pid/cmdline " );
838 my @args = split ( /\0/ , $cmdline );
840 # serach for lxc-console -n <vmid>
841 return if scalar ( @args ) != 3 ;
842 return if $args [ 1 ] ne '-n' ;
843 return if $args [ 2 ] !~ m/^\d+$/ ;
844 return if $args [ 0 ] !~ m
|^( /usr/ bin
/) ?lxc-console
$|;
848 push @{ $res ->{ $vmid }}, $pid ;
854 my $ipv4_reverse_mask = [
890 # Note: we cannot use Net:IP, because that only allows strict
892 sub parse_ipv4_cidr
{
893 my ( $cidr, $noerr ) = @_ ;
895 if ( $cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ( $2 > 7 ) && ( $2 < 32 )) {
896 return { address
=> $1, netmask
=> $ipv4_reverse_mask ->[ $2 ] };
899 return undef if $noerr ;
901 die "unable to parse ipv4 address/mask \n " ;
904 sub lxc_conf_to_pve
{
905 my ( $vmid, $lxc_conf ) = @_ ;
907 my $properties = json_config_properties
();
909 my $conf = { digest
=> $lxc_conf ->{ digest
} };
911 foreach my $k ( keys %$properties ) {
913 if ( $k eq 'description' ) {
914 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
915 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
917 } elsif ( $k eq 'onboot' ) {
918 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
919 } elsif ( $k eq 'startup' ) {
920 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
921 } elsif ( $k eq 'hostname' ) {
922 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
923 } elsif ( $k eq 'nameserver' ) {
924 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
925 } elsif ( $k eq 'searchdomain' ) {
926 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
927 } elsif ( $k eq 'memory' ) {
928 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
929 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
931 } elsif ( $k eq 'swap' ) {
932 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
933 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
934 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
936 } elsif ( $k eq 'cpulimit' ) {
937 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
938 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
940 if ( $cfs_period_us && $cfs_quota_us ) {
941 $conf ->{ $k } = $cfs_quota_us/$cfs_period_us ;
945 } elsif ( $k eq 'cpuunits' ) {
946 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
947 } elsif ( $k eq 'disk' ) {
948 $conf ->{ $k } = defined ( $lxc_conf ->{ 'pve.disksize' }) ?
949 $lxc_conf ->{ 'pve.disksize' } : 0 ;
950 } elsif ( $k =~ m/^net\d$/ ) {
951 my $net = $lxc_conf ->{ $k };
953 $conf ->{ $k } = print_lxc_network
( $net );
960 # verify and cleanup nameserver list (replace \0 with ' ')
961 sub verify_nameserver_list
{
962 my ( $nameserver_list ) = @_ ;
965 foreach my $server ( PVE
:: Tools
:: split_list
( $nameserver_list )) {
966 PVE
:: JSONSchema
:: pve_verify_ip
( $server );
970 return join ( ' ' , @list );
973 sub verify_searchdomain_list
{
974 my ( $searchdomain_list ) = @_ ;
977 foreach my $server ( PVE
:: Tools
:: split_list
( $searchdomain_list )) {
978 # todo: should we add checks for valid dns domains?
982 return join ( ' ' , @list );
985 sub update_lxc_config
{
986 my ( $vmid, $conf, $running, $param, $delete ) = @_ ;
990 if ( defined ( $delete )) {
991 foreach my $opt ( @$delete ) {
992 if ( $opt eq 'hostname' || $opt eq 'memory' ) {
993 die "unable to delete required option ' $opt ' \n " ;
994 } elsif ( $opt eq 'swap' ) {
995 delete $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' };
996 write_cgroup_value
( "memory" , $vmid, "memory.memsw.limit_in_bytes" , - 1 );
997 } elsif ( $opt eq 'description' ) {
998 delete $conf ->{ 'pve.comment' };
999 } elsif ( $opt eq 'onboot' ) {
1000 delete $conf ->{ 'pve.onboot' };
1001 } elsif ( $opt eq 'startup' ) {
1002 delete $conf ->{ 'pve.startup' };
1003 } elsif ( $opt eq 'nameserver' ) {
1004 delete $conf ->{ 'pve.nameserver' };
1005 push @nohotplug, $opt ;
1007 } elsif ( $opt eq 'searchdomain' ) {
1008 delete $conf ->{ 'pve.searchdomain' };
1009 push @nohotplug, $opt ;
1011 } elsif ( $opt =~ m/^net\d$/ ) {
1012 delete $conf ->{ $opt };
1013 push @nohotplug, $opt ;
1018 PVE
:: LXC
:: write_config
( $vmid, $conf ) if $running ;
1022 foreach my $opt ( keys %$param ) {
1023 my $value = $param ->{ $opt };
1024 if ( $opt eq 'hostname' ) {
1025 $conf ->{ 'lxc.utsname' } = $value ;
1026 } elsif ( $opt eq 'onboot' ) {
1027 $conf ->{ 'pve.onboot' } = $value ?
1 : 0 ;
1028 } elsif ( $opt eq 'startup' ) {
1029 $conf ->{ 'pve.startup' } = $value ;
1030 } elsif ( $opt eq 'nameserver' ) {
1031 my $list = verify_nameserver_list
( $value );
1032 $conf ->{ 'pve.nameserver' } = $list ;
1033 push @nohotplug, $opt ;
1035 } elsif ( $opt eq 'searchdomain' ) {
1036 my $list = verify_searchdomain_list
( $value );
1037 $conf ->{ 'pve.searchdomain' } = $list ;
1038 push @nohotplug, $opt ;
1040 } elsif ( $opt eq 'memory' ) {
1041 $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } = $value*1024*1024 ;
1042 write_cgroup_value
( "memory" , $vmid, "memory.limit_in_bytes" , $value*1024*1024 );
1043 } elsif ( $opt eq 'swap' ) {
1044 my $mem = $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' };
1045 $mem = $param ->{ memory
}* 1024 * 1024 if $param ->{ memory
};
1046 $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' } = $mem + $value*1024*1024 ;
1047 write_cgroup_value
( "memory" , $vmid, "memory.memsw.limit_in_bytes" , $mem + $value*1024*1024 );
1049 } elsif ( $opt eq 'cpulimit' ) {
1051 my $cfs_period_us = 100000 ;
1052 $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' } = $cfs_period_us ;
1053 $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' } = $cfs_period_us*$value ;
1054 write_cgroup_value
( "cpu" , $vmid, "cpu.cfs_quota_us" , $cfs_period_us*$value );
1056 delete $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
1057 delete $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
1058 write_cgroup_value
( "cpu" , $vmid, "cpu.cfs_quota_us" , - 1 );
1060 } elsif ( $opt eq 'cpuunits' ) {
1061 $conf ->{ 'lxc.cgroup.cpu.shares' } = $value ;
1062 write_cgroup_value
( "cpu" , $vmid, "cpu.shares" , $value );
1063 } elsif ( $opt eq 'description' ) {
1064 $conf ->{ 'pve.comment' } = PVE
:: Tools
:: encode_text
( $value );
1065 } elsif ( $opt eq 'disk' ) {
1066 $conf ->{ 'pve.disksize' } = $value ;
1067 push @nohotplug, $opt ;
1069 } elsif ( $opt =~ m/^net(\d+)$/ ) {
1071 my $net = PVE
:: LXC
:: parse_lxc_network
( $value );
1072 $net ->{ 'veth.pair' } = "veth${vmid}. $netid " ;
1073 $conf ->{ $opt } = $net ;
1074 push @nohotplug, $opt ;
1079 PVE
:: LXC
:: write_config
( $vmid, $conf ) if $running ;
1082 die "unable to modify " . join ( "," , @nohotplug ). " while container is running \n " if @nohotplug > 0 && $running ;
1085 sub get_primary_ips
{
1088 # return data from net0
1090 my $net = $conf ->{ net0
};
1091 return undef if ! $net ;
1093 my $ipv4 = $net ->{ ip
};
1094 $ipv4 =~ s!/\d+$!! if $ipv4 ;
1095 my $ipv6 = $net ->{ ip
};
1096 $ipv6 =~ s!/\d+$!! if $ipv6 ;
1098 return ( $ipv4, $ipv6 );
1101 sub destory_lxc_container
{
1102 my ( $storage_cfg, $vmid, $conf ) = @_ ;
1104 if ( my $volid = $conf ->{ 'pve.volid' }) {
1106 my ( $vtype, $name, $owner ) = PVE
:: Storage
:: parse_volname
( $storage_cfg, $volid );
1107 die "got strange volid (containe is not owner!) \n " if $vmid != $owner ;
1109 PVE
:: Storage
:: vdisk_free
( $storage_cfg, $volid );
1111 destroy_config
( $vmid );
1114 my $cmd = [ 'lxc-destroy' , '-n' , $vmid ];
1116 PVE
:: Tools
:: run_command
( $cmd );