]>
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 ,
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 = {
154 sub write_lxc_config
{
155 my ( $filename, $data ) = @_ ;
159 return $raw if ! $data ;
161 my $done_hash = { digest
=> 1 };
163 foreach my $k ( sort keys %$data ) {
164 next if $k !~ m/^lxc\./ ;
165 $done_hash ->{ $k } = 1 ;
166 if ( ref ( $data ->{ $k })) {
167 die "got unexpected reference for ' $k '" if ! $lxc_array_configs ->{ $k };
168 foreach my $v (@{ $data ->{ $k }}) {
172 $raw .= " $k = $data ->{ $k } \n " ;
176 foreach my $k ( sort keys %$data ) {
177 next if $k !~ m/^pve\./ ;
178 $done_hash ->{ $k } = 1 ;
179 $raw .= " $k = $data ->{ $k } \n " ;
182 foreach my $k ( sort keys %$data ) {
183 next if $k !~ m/^net\d+$/ ;
184 $done_hash ->{ $k } = 1 ;
185 my $net = $data ->{ $k };
186 $raw .= "lxc.network.type = $net ->{type} \n " ;
187 foreach my $subkey ( sort keys %$net ) {
188 next if $subkey eq 'type' ;
189 if ( $valid_lxc_network_keys ->{ $subkey }) {
190 $raw .= "lxc.network. $subkey = $net ->{ $subkey } \n " ;
191 } elsif ( $valid_pve_network_keys ->{ $subkey }) {
192 $raw .= "pve.network. $subkey = $net ->{ $subkey } \n " ;
194 die "found invalid network key ' $subkey '" ;
199 foreach my $k ( sort keys %$data ) {
200 next if $done_hash ->{ $k };
201 die "found un-written value in config - implement this!" ;
207 sub parse_lxc_option
{
208 my ( $name, $value ) = @_ ;
210 my $parser = $valid_lxc_keys ->{ $name };
212 die "inavlid key ' $name ' \n " if ! defined ( $parser );
214 if ( $parser eq '1' ) {
216 } elsif ( ref ( $parser )) {
217 my $res = & $parser ( $name, $value );
218 return $res if defined ( $res );
221 return $value if $value =~ m/^$parser$/ ;
224 die "unable to parse value ' $value ' for option ' $name ' \n " ;
227 sub parse_lxc_config
{
228 my ( $filename, $raw ) = @_ ;
230 return undef if ! defined ( $raw );
233 digest
=> Digest
:: SHA
:: sha1_hex
( $raw ),
236 $filename =~ m
| /lxc/ ( \d
+)/ config
$|
237 || die "got strange filename ' $filename '" ;
241 my $network_counter = 0 ;
242 my $network_list = [];
243 my $host_ifnames = {};
245 my $find_next_hostif_name = sub {
246 for ( my $i = 0 ; $i < 10 ; $i++ ) {
247 my $name = "veth${vmid}. $i " ;
248 if (! $host_ifnames ->{ $name }) {
249 $host_ifnames ->{ $name } = 1 ;
254 die "unable to find free host_ifname" ; # should not happen
257 my $push_network = sub {
260 push @{ $network_list }, $netconf ;
262 if ( my $netname = $netconf ->{ 'veth.pair' }) {
263 if ( $netname =~ m/^veth(\d+).(\d)$/ ) {
264 die "wrong vmid for network interface pair \n " if $1 != $vmid ;
265 my $host_ifnames ->{ $netname } = 1 ;
267 die "wrong network interface pair \n " ;
274 while ( $raw && $raw =~ s/^(.*?)(\n|$)// ) {
277 next if $line =~ m/^\#/ ;
278 next if $line =~ m/^\s*$/ ;
280 if ( $line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
281 my ( $subkey, $value ) = ( $1, $2 );
282 if ( $subkey eq 'type' ) {
283 & $push_network ( $network );
284 $network = { type
=> $value };
285 } elsif ( $valid_lxc_network_keys ->{ $subkey }) {
286 $network ->{ $subkey } = $value ;
288 die "unable to parse config line: $line\n " ;
292 if ( $line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
293 my ( $subkey, $value ) = ( $1, $2 );
294 if ( $valid_pve_network_keys ->{ $subkey }) {
295 $network ->{ $subkey } = $value ;
297 die "unable to parse config line: $line\n " ;
301 if ( $line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/ ) {
302 my ( $name, $value ) = ( $1, $2 );
303 $data ->{ $name } = $value ;
306 if ( $line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/ ) {
307 my ( $name, $value ) = ( $1, $2 );
309 if ( $lxc_array_configs ->{ $name }) {
310 $data ->{ $name } = [] if ! defined ( $data ->{ $name });
311 push @{ $data ->{ $name }}, parse_lxc_option
( $name, $value );
313 die "multiple definitions for $name\n " if defined ( $data ->{ $name });
314 $data ->{ $name } = parse_lxc_option
( $name, $value );
320 die "unable to parse config line: $line\n " ;
323 & $push_network ( $network );
325 foreach my $net (@{ $network_list }) {
326 $net ->{ 'veth.pair' } = & $find_next_hostif_name () if ! $net ->{ 'veth.pair' };
327 $net ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $net ->{ hwaddr
};
328 die "unsupported network type ' $net ->{type}' \n " if $net ->{ type
} ne 'veth' ;
330 if ( $net ->{ 'veth.pair' } =~ m/^veth\d+.(\d+)$/ ) {
331 $data ->{ "net $1 " } = $net ;
339 my $vmlist = PVE
:: Cluster
:: get_vmlist
();
341 return $res if ! $vmlist || ! $vmlist ->{ ids
};
342 my $ids = $vmlist ->{ ids
};
344 foreach my $vmid ( keys %$ids ) {
345 next if ! $vmid ; # skip CT0
346 my $d = $ids ->{ $vmid };
347 next if ! $d ->{ node
} || $d ->{ node
} ne $nodename ;
348 next if ! $d ->{ type
} || $d ->{ type
} ne 'lxc' ;
349 $res ->{ $vmid }->{ type
} = 'lxc' ;
354 sub cfs_config_path
{
355 my ( $vmid, $node ) = @_ ;
357 $node = $nodename if ! $node ;
358 return "nodes/ $node/lxc/$vmid/config " ;
362 my ( $vmid, $node ) = @_ ;
364 my $cfspath = cfs_config_path
( $vmid, $node );
365 return "/etc/pve/ $cfspath " ;
371 my $cfspath = cfs_config_path
( $vmid );
373 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath );
374 die "container $vmid does not exists \n " if ! defined ( $conf );
380 my ( $vmid, $conf ) = @_ ;
382 my $dir = "/etc/pve/nodes/ $nodename/lxc " ;
386 mkdir ( $dir ) || die "unable to create container configuration directory - $!\n " ;
388 write_config
( $vmid, $conf );
394 my $dir = "/etc/pve/nodes/ $nodename/lxc/$vmid " ;
395 File
:: Path
:: rmtree
( $dir );
399 my ( $vmid, $conf ) = @_ ;
401 my $cfspath = cfs_config_path
( $vmid );
403 PVE
:: Cluster
:: cfs_write_file
( $cfspath, $conf );
407 sub write_temp_config
{
408 my ( $vmid, $conf ) = @_ ;
411 my $filename = "/tmp/temp-lxc-conf- $vmid - $$ - $tempcounter .conf" ;
413 my $raw = write_lxc_config
( $filename, $conf );
415 PVE
:: Tools
:: file_set_contents
( $filename, $raw );
420 # flock: we use one file handle per process, so lock file
421 # can be called multiple times and succeeds for the same process.
423 my $lock_handles = {};
424 my $lockdir = "/run/lock/lxc" ;
429 return " $lockdir/pve -config-{ $vmid }.lock" ;
433 my ( $vmid, $timeout ) = @_ ;
435 $timeout = 10 if ! $timeout ;
438 my $filename = lock_filename
( $vmid );
440 mkdir $lockdir if !- d
$lockdir ;
442 my $lock_func = sub {
443 if (! $lock_handles ->{ $$ }->{ $filename }) {
444 my $fh = new IO
:: File
( ">> $filename " ) ||
445 die "can't open file - $!\n " ;
446 $lock_handles ->{ $$ }->{ $filename } = { fh
=> $fh, refcount
=> 0 };
449 if (! flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode | LOCK_NB
)) {
450 print STDERR
"trying to aquire lock..." ;
453 $success = flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode );
454 # try again on EINTR (see bug #273)
455 if ( $success || ( $! != EINTR
)) {
460 print STDERR
" failed \n " ;
461 die "can't aquire lock - $!\n " ;
464 $lock_handles ->{ $$ }->{ $filename }->{ refcount
}++;
466 print STDERR
" OK \n " ;
470 eval { PVE
:: Tools
:: run_with_timeout
( $timeout, $lock_func ); };
473 die "can't lock file ' $filename ' - $err " ;
480 my $filename = lock_filename
( $vmid );
482 if ( my $fh = $lock_handles ->{ $$ }->{ $filename }->{ fh
}) {
483 my $refcount = -- $lock_handles ->{ $$ }->{ $filename }->{ refcount
};
484 if ( $refcount <= 0 ) {
485 $lock_handles ->{ $$ }->{ $filename } = undef ;
492 my ( $vmid, $timeout, $code, @param ) = @_ ;
496 lock_aquire
( $vmid, $timeout );
497 eval { $res = & $code ( @param ) };
510 description
=> "Specifies whether a VM will be started during system bootup." ,
513 startup
=> get_standard_option
( 'pve-startup-order' ),
517 description
=> "The number of CPUs for this container (0 is unlimited)." ,
525 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." ,
533 description
=> "Amount of RAM for the VM in MB." ,
540 description
=> "Amount of SWAP for the VM in MB." ,
547 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
553 description
=> "Set a host name for the container." ,
560 description
=> "Container description. Only used on the configuration web interface." ,
565 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
570 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." ,
574 my $MAX_LXC_NETWORKS = 10 ;
575 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
576 $confdesc ->{ "net $i " } = {
578 type
=> 'string' , format
=> 'pve-lxc-network' ,
579 description
=> "Specifies network interfaces for the container." ,
586 return defined ( $confdesc ->{ $name });
589 # add JSON properties for create and set function
590 sub json_config_properties
{
593 foreach my $opt ( keys %$confdesc ) {
594 $prop ->{ $opt } = $confdesc ->{ $opt };
600 # container status helpers
602 sub list_active_containers
{
604 my $filename = "/proc/net/unix" ;
606 # similar test is used by lcxcontainers.c: list_active_containers
609 my $fh = IO
:: File-
> new ( $filename, "r" );
612 while ( defined ( my $line = < $fh >)) {
613 if ( $line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/ ) {
615 if ( $path =~ m!^@/etc/pve/lxc/(\d+)/command$! ) {
626 # warning: this is slow
630 my $active_hash = list_active_containers
();
632 return 1 if defined ( $active_hash ->{ $vmid });
637 sub get_container_disk_usage
{
640 my $cmd = [ 'lxc-attach' , '-n' , $vmid, '--' , 'df' , '-P' , '-B' , '1' , '/' ];
650 if ( my ( $fsid, $total, $used, $avail ) = $line =~
651 m/^(\S+.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s.*$/ ) {
659 eval { PVE
:: Tools
:: run_command
( $cmd, timeout
=> 1 , outfunc
=> $parser ); };
668 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'lxc' }} : config_list
();
670 my $active_hash = list_active_containers
();
672 foreach my $vmid ( keys %$list ) {
673 my $d = $list ->{ $vmid };
675 my $running = defined ( $active_hash ->{ $vmid });
677 $d ->{ status
} = $running ?
'running' : 'stopped' ;
679 my $cfspath = cfs_config_path
( $vmid );
680 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath ) || {};
682 $d ->{ name
} = $conf ->{ 'lxc.utsname' } || "CT $vmid " ;
683 $d ->{ name
} =~ s/[\s]//g ;
687 my $cfs_period_us = $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
688 my $cfs_quota_us = $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
690 if ( $cfs_period_us && $cfs_quota_us ) {
691 $d ->{ cpus
} = int ( $cfs_quota_us/$cfs_period_us );
695 $d ->{ maxdisk
} = defined ( $conf ->{ 'pve.disksize' }) ?
696 int ( $conf ->{ 'pve.disksize' }* 1024 * 1024 )* 1024 : 1024 * 1024 * 1024 * 1024 * 1024 ;
698 if ( my $private = $conf ->{ 'lxc.rootfs' }) {
699 if ( $private =~ m!^/! ) {
700 my $res = PVE
:: Tools
:: df
( $private, 2 );
701 $d ->{ disk
} = $res ->{ used
};
702 $d ->{ maxdisk
} = $res ->{ total
};
704 if ( $private =~ m!^(?:loop|nbd):(?:\S+)$! ) {
705 my $res = get_container_disk_usage
( $vmid );
706 $d ->{ disk
} = $res ->{ used
};
707 $d ->{ maxdisk
} = $res ->{ total
};
714 $d ->{ maxmem
} = ( $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }|| 0 ) +
715 ( $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }|| 0 );
727 foreach my $vmid ( keys %$list ) {
728 my $d = $list ->{ $vmid };
729 next if $d ->{ status
} ne 'running' ;
731 $d ->{ uptime
} = 100 ; # fixme:
733 $d ->{ mem
} = read_cgroup_value
( 'memory' , $vmid, 'memory.usage_in_bytes' );
734 $d ->{ swap
} = read_cgroup_value
( 'memory' , $vmid, 'memory.memsw.usage_in_bytes' ) - $d ->{ mem
};
741 sub print_lxc_network
{
744 die "no network bridge defined \n " if ! $net ->{ bridge
};
746 my $res = "bridge= $net ->{bridge}" ;
748 foreach my $k ( qw(hwaddr mtu name ip gw ip6 gw6 firewall tag) ) {
749 next if ! defined ( $net ->{ $k });
750 $res .= ", $k = $net ->{ $k }" ;
756 sub parse_lxc_network
{
761 return $res if ! $data ;
763 foreach my $pv ( split ( /,/ , $data )) {
764 if ( $pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/ ) {
771 $res ->{ type
} = 'veth' ;
772 $res ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $res ->{ mac
};
777 sub read_cgroup_value
{
778 my ( $group, $vmid, $name, $full ) = @_ ;
780 my $path = "/sys/fs/cgroup/ $group/lxc/$vmid/$name " ;
782 return PVE
:: Tools
:: file_get_contents
( $path ) if $full ;
784 return PVE
:: Tools
:: file_read_firstline
( $path );
787 sub find_lxc_console_pids
{
791 PVE
:: Tools
:: dir_glob_foreach
( '/proc' , '\d+' , sub {
794 my $cmdline = PVE
:: Tools
:: file_read_firstline
( "/proc/ $pid/cmdline " );
797 my @args = split ( /\0/ , $cmdline );
799 # serach for lxc-console -n <vmid>
800 return if scalar ( @args ) != 3 ;
801 return if $args [ 1 ] ne '-n' ;
802 return if $args [ 2 ] !~ m/^\d+$/ ;
803 return if $args [ 0 ] !~ m
|^( /usr/ bin
/) ?lxc-console
$|;
807 push @{ $res ->{ $vmid }}, $pid ;
813 my $ipv4_reverse_mask = [
849 # Note: we cannot use Net:IP, because that only allows strict
851 sub parse_ipv4_cidr
{
852 my ( $cidr, $noerr ) = @_ ;
854 if ( $cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ( $2 > 7 ) && ( $2 < 32 )) {
855 return { address
=> $1, netmask
=> $ipv4_reverse_mask ->[ $2 ] };
858 return undef if $noerr ;
860 die "unable to parse ipv4 address/mask \n " ;
863 sub lxc_conf_to_pve
{
864 my ( $vmid, $lxc_conf ) = @_ ;
866 my $properties = json_config_properties
();
868 my $conf = { digest
=> $lxc_conf ->{ digest
} };
870 foreach my $k ( keys %$properties ) {
872 if ( $k eq 'description' ) {
873 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
874 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
876 } elsif ( $k eq 'onboot' ) {
877 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
878 } elsif ( $k eq 'startup' ) {
879 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
880 } elsif ( $k eq 'hostname' ) {
881 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
882 } elsif ( $k eq 'nameserver' ) {
883 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
884 } elsif ( $k eq 'searchdomain' ) {
885 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
886 } elsif ( $k eq 'memory' ) {
887 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
888 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
890 } elsif ( $k eq 'swap' ) {
891 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
892 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
893 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
895 } elsif ( $k eq 'cpus' ) {
896 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
897 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
899 if ( $cfs_period_us && $cfs_quota_us ) {
900 $conf ->{ $k } = int ( $cfs_quota_us/$cfs_period_us );
904 } elsif ( $k eq 'cpuunits' ) {
905 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
906 } elsif ( $k eq 'disk' ) {
907 $conf ->{ $k } = defined ( $lxc_conf ->{ 'pve.disksize' }) ?
908 $lxc_conf ->{ 'pve.disksize' } : 0 ;
909 } elsif ( $k =~ m/^net\d$/ ) {
910 my $net = $lxc_conf ->{ $k };
912 $conf ->{ $k } = print_lxc_network
( $net );
919 # verify and cleanup nameserver list (replace \0 with ' ')
920 sub verify_nameserver_list
{
921 my ( $nameserver_list ) = @_ ;
924 foreach my $server ( PVE
:: Tools
:: split_list
( $nameserver_list )) {
925 PVE
:: JSONSchema
:: pve_verify_ip
( $server );
929 return join ( ' ' , @list );
932 sub verify_searchdomain_list
{
933 my ( $searchdomain_list ) = @_ ;
936 foreach my $server ( PVE
:: Tools
:: split_list
( $searchdomain_list )) {
937 # todo: should we add checks for valid dns domains?
941 return join ( ' ' , @list );
944 sub update_lxc_config
{
945 my ( $vmid, $conf, $running, $param, $delete ) = @_ ;
948 die "unable to modify config while container is running \n " if $running ;
950 if ( defined ( $delete )) {
951 foreach my $opt ( @$delete ) {
952 if ( $opt eq 'hostname' || $opt eq 'memory' ) {
953 die "unable to delete required option ' $opt ' \n " ;
954 } elsif ( $opt eq 'swap' ) {
955 delete $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' };
956 } elsif ( $opt eq 'description' ) {
957 delete $conf ->{ 'pve.comment' };
958 } elsif ( $opt eq 'onboot' ) {
959 delete $conf ->{ 'pve.onboot' };
960 } elsif ( $opt eq 'startup' ) {
961 delete $conf ->{ 'pve.startup' };
962 } elsif ( $opt eq 'nameserver' ) {
963 delete $conf ->{ 'pve.nameserver' };
964 } elsif ( $opt eq 'searchdomain' ) {
965 delete $conf ->{ 'pve.searchdomain' };
966 } elsif ( $opt =~ m/^net\d$/ ) {
967 delete $conf ->{ $opt };
974 foreach my $opt ( keys %$param ) {
975 my $value = $param ->{ $opt };
976 if ( $opt eq 'hostname' ) {
977 $conf ->{ 'lxc.utsname' } = $value ;
978 } elsif ( $opt eq 'onboot' ) {
979 $conf ->{ 'pve.onboot' } = $value ?
1 : 0 ;
980 } elsif ( $opt eq 'startup' ) {
981 $conf ->{ 'pve.startup' } = $value ;
982 } elsif ( $opt eq 'nameserver' ) {
983 my $list = verify_nameserver_list
( $value );
984 $conf ->{ 'pve.nameserver' } = $list ;
985 } elsif ( $opt eq 'searchdomain' ) {
986 my $list = verify_searchdomain_list
( $value );
987 $conf ->{ 'pve.searchdomain' } = $list ;
988 } elsif ( $opt eq 'memory' ) {
989 $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } = $value*1024*1024 ;
990 } elsif ( $opt eq 'swap' ) {
991 my $mem = $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' };
992 $mem = $param ->{ memory
}* 1024 * 1024 if $param ->{ memory
};
993 $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' } = $mem + $value*1024*1024 ;
994 } elsif ( $opt eq 'cpus' ) {
996 my $cfs_period_us = 100000 ;
997 $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' } = $cfs_period_us ;
998 $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' } = $cfs_period_us*$value ;
1000 delete $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
1001 delete $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
1003 } elsif ( $opt eq 'cpuunits' ) {
1004 $conf ->{ 'lxc.cgroup.cpu.shares' } = $value ;
1005 } elsif ( $opt eq 'description' ) {
1006 $conf ->{ 'pve.comment' } = PVE
:: Tools
:: encode_text
( $value );
1007 } elsif ( $opt eq 'disk' ) {
1008 $conf ->{ 'pve.disksize' } = $value ;
1009 } elsif ( $opt =~ m/^net(\d+)$/ ) {
1011 my $net = PVE
:: LXC
:: parse_lxc_network
( $value );
1012 $net ->{ 'veth.pair' } = "veth${vmid}. $netid " ;
1013 $conf ->{ $opt } = $net ;
1020 sub get_primary_ips
{
1023 # return data from net0
1025 my $net = $conf ->{ net0
};
1026 return undef if ! $net ;
1028 my $ipv4 = $net ->{ ip
};
1029 $ipv4 =~ s!/\d+$!! if $ipv4 ;
1030 my $ipv6 = $net ->{ ip
};
1031 $ipv6 =~ s!/\d+$!! if $ipv6 ;
1033 return ( $ipv4, $ipv6 );
1036 sub destory_lxc_container
{
1037 my ( $storage_cfg, $vmid, $conf ) = @_ ;
1039 if ( my $volid = $conf ->{ 'pve.volid' }) {
1041 my ( $vtype, $name, $owner ) = PVE
:: Storage
:: parse_volname
( $storage_cfg, $volid );
1042 die "got strange volid (containe is not owner!) \n " if $vmid != $owner ;
1044 PVE
:: Storage
:: vdisk_free
( $storage_cfg, $volid );
1046 destroy_config
( $vmid );
1049 my $cmd = [ 'lxc-destroy' , '-n' , $vmid ];
1051 PVE
:: Tools
:: run_command
( $cmd );