]>
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 my $lock_func = sub {
441 if (! $lock_handles ->{ $$ }->{ $filename }) {
442 my $fh = new IO
:: File
( ">> $filename " ) ||
443 die "can't open file - $!\n " ;
444 $lock_handles ->{ $$ }->{ $filename } = { fh
=> $fh, refcount
=> 0 };
447 if (! flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode | LOCK_NB
)) {
448 print STDERR
"trying to aquire lock..." ;
451 $success = flock ( $lock_handles ->{ $$ }->{ $filename }->{ fh
}, $mode );
452 # try again on EINTR (see bug #273)
453 if ( $success || ( $! != EINTR
)) {
458 print STDERR
" failed \n " ;
459 die "can't aquire lock - $!\n " ;
462 $lock_handles ->{ $$ }->{ $filename }->{ refcount
}++;
464 print STDERR
" OK \n " ;
468 eval { PVE
:: Tools
:: run_with_timeout
( $timeout, $lock_func ); };
471 die "can't lock file ' $filename ' - $err " ;
478 my $filename = lock_filename
( $vmid );
480 if ( my $fh = $lock_handles ->{ $$ }->{ $filename }->{ fh
}) {
481 my $refcount = -- $lock_handles ->{ $$ }->{ $filename }->{ refcount
};
482 if ( $refcount <= 0 ) {
483 $lock_handles ->{ $$ }->{ $filename } = undef ;
490 my ( $vmid, $timeout, $code, @param ) = @_ ;
494 lock_aquire
( $vmid, $timeout );
495 eval { $res = & $code ( @param ) };
508 description
=> "Specifies whether a VM will be started during system bootup." ,
511 startup
=> get_standard_option
( 'pve-startup-order' ),
515 description
=> "The number of CPUs for this container (0 is unlimited)." ,
523 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." ,
531 description
=> "Amount of RAM for the VM in MB." ,
538 description
=> "Amount of SWAP for the VM in MB." ,
545 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
551 description
=> "Set a host name for the container." ,
558 description
=> "Container description. Only used on the configuration web interface." ,
563 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
568 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." ,
572 my $MAX_LXC_NETWORKS = 10 ;
573 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
574 $confdesc ->{ "net $i " } = {
576 type
=> 'string' , format
=> 'pve-lxc-network' ,
577 description
=> "Specifies network interfaces for the container." ,
584 return defined ( $confdesc ->{ $name });
587 # add JSON properties for create and set function
588 sub json_config_properties
{
591 foreach my $opt ( keys %$confdesc ) {
592 $prop ->{ $opt } = $confdesc ->{ $opt };
598 # container status helpers
600 sub list_active_containers
{
602 my $filename = "/proc/net/unix" ;
604 # similar test is used by lcxcontainers.c: list_active_containers
607 my $fh = IO
:: File-
> new ( $filename, "r" );
610 while ( defined ( my $line = < $fh >)) {
611 if ( $line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/ ) {
613 if ( $path =~ m!^@/etc/pve/lxc/(\d+)/command$! ) {
624 # warning: this is slow
628 my $active_hash = list_active_containers
();
630 return 1 if defined ( $active_hash ->{ $vmid });
635 sub get_container_disk_usage
{
638 my $cmd = [ 'lxc-attach' , '-n' , $vmid, '--' , 'df' , '-P' , '-B' , '1' , '/' ];
648 if ( my ( $fsid, $total, $used, $avail ) = $line =~
649 m/^(\S+.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s.*$/ ) {
657 eval { PVE
:: Tools
:: run_command
( $cmd, timeout
=> 1 , outfunc
=> $parser ); };
666 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'lxc' }} : config_list
();
668 my $active_hash = list_active_containers
();
670 foreach my $vmid ( keys %$list ) {
671 my $d = $list ->{ $vmid };
673 my $running = defined ( $active_hash ->{ $vmid });
675 $d ->{ status
} = $running ?
'running' : 'stopped' ;
677 my $cfspath = cfs_config_path
( $vmid );
678 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath ) || {};
680 $d ->{ name
} = $conf ->{ 'lxc.utsname' } || "CT $vmid " ;
681 $d ->{ name
} =~ s/[\s]//g ;
685 my $cfs_period_us = $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
686 my $cfs_quota_us = $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
688 if ( $cfs_period_us && $cfs_quota_us ) {
689 $d ->{ cpus
} = int ( $cfs_quota_us/$cfs_period_us );
693 $d ->{ maxdisk
} = defined ( $conf ->{ 'pve.disksize' }) ?
694 int ( $conf ->{ 'pve.disksize' }* 1024 * 1024 )* 1024 : 1024 * 1024 * 1024 * 1024 * 1024 ;
696 if ( my $private = $conf ->{ 'lxc.rootfs' }) {
697 if ( $private =~ m!^/! ) {
698 my $res = PVE
:: Tools
:: df
( $private, 2 );
699 $d ->{ disk
} = $res ->{ used
};
700 $d ->{ maxdisk
} = $res ->{ total
};
702 if ( $private =~ m!^(?:loop|nbd):(?:\S+)$! ) {
703 my $res = get_container_disk_usage
( $vmid );
704 $d ->{ disk
} = $res ->{ used
};
705 $d ->{ maxdisk
} = $res ->{ total
};
712 $d ->{ maxmem
} = ( $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }|| 0 ) +
713 ( $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }|| 0 );
725 foreach my $vmid ( keys %$list ) {
726 my $d = $list ->{ $vmid };
727 next if $d ->{ status
} ne 'running' ;
729 $d ->{ uptime
} = 100 ; # fixme:
731 $d ->{ mem
} = read_cgroup_value
( 'memory' , $vmid, 'memory.usage_in_bytes' );
732 $d ->{ swap
} = read_cgroup_value
( 'memory' , $vmid, 'memory.memsw.usage_in_bytes' ) - $d ->{ mem
};
739 sub print_lxc_network
{
742 die "no network bridge defined \n " if ! $net ->{ bridge
};
744 my $res = "bridge= $net ->{bridge}" ;
746 foreach my $k ( qw(hwaddr mtu name ip gw ip6 gw6 firewall tag) ) {
747 next if ! defined ( $net ->{ $k });
748 $res .= ", $k = $net ->{ $k }" ;
754 sub parse_lxc_network
{
759 return $res if ! $data ;
761 foreach my $pv ( split ( /,/ , $data )) {
762 if ( $pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/ ) {
769 $res ->{ type
} = 'veth' ;
770 $res ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $res ->{ mac
};
775 sub read_cgroup_value
{
776 my ( $group, $vmid, $name, $full ) = @_ ;
778 my $path = "/sys/fs/cgroup/ $group/lxc/$vmid/$name " ;
780 return PVE
:: Tools
:: file_get_contents
( $path ) if $full ;
782 return PVE
:: Tools
:: file_read_firstline
( $path );
785 sub find_lxc_console_pids
{
789 PVE
:: Tools
:: dir_glob_foreach
( '/proc' , '\d+' , sub {
792 my $cmdline = PVE
:: Tools
:: file_read_firstline
( "/proc/ $pid/cmdline " );
795 my @args = split ( /\0/ , $cmdline );
797 # serach for lxc-console -n <vmid>
798 return if scalar ( @args ) != 3 ;
799 return if $args [ 1 ] ne '-n' ;
800 return if $args [ 2 ] !~ m/^\d+$/ ;
801 return if $args [ 0 ] !~ m
|^( /usr/ bin
/) ?lxc-console
$|;
805 push @{ $res ->{ $vmid }}, $pid ;
811 my $ipv4_reverse_mask = [
847 # Note: we cannot use Net:IP, because that only allows strict
849 sub parse_ipv4_cidr
{
850 my ( $cidr, $noerr ) = @_ ;
852 if ( $cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ( $2 > 7 ) && ( $2 < 32 )) {
853 return { address
=> $1, netmask
=> $ipv4_reverse_mask ->[ $2 ] };
856 return undef if $noerr ;
858 die "unable to parse ipv4 address/mask \n " ;
861 sub lxc_conf_to_pve
{
862 my ( $vmid, $lxc_conf ) = @_ ;
864 my $properties = json_config_properties
();
866 my $conf = { digest
=> $lxc_conf ->{ digest
} };
868 foreach my $k ( keys %$properties ) {
870 if ( $k eq 'description' ) {
871 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
872 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
874 } elsif ( $k eq 'onboot' ) {
875 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
876 } elsif ( $k eq 'startup' ) {
877 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
878 } elsif ( $k eq 'hostname' ) {
879 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
880 } elsif ( $k eq 'nameserver' ) {
881 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
882 } elsif ( $k eq 'searchdomain' ) {
883 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
884 } elsif ( $k eq 'memory' ) {
885 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
886 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
888 } elsif ( $k eq 'swap' ) {
889 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
890 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
891 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
893 } elsif ( $k eq 'cpus' ) {
894 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
895 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
897 if ( $cfs_period_us && $cfs_quota_us ) {
898 $conf ->{ $k } = int ( $cfs_quota_us/$cfs_period_us );
902 } elsif ( $k eq 'cpuunits' ) {
903 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
904 } elsif ( $k eq 'disk' ) {
905 $conf ->{ $k } = defined ( $lxc_conf ->{ 'pve.disksize' }) ?
906 $lxc_conf ->{ 'pve.disksize' } : 0 ;
907 } elsif ( $k =~ m/^net\d$/ ) {
908 my $net = $lxc_conf ->{ $k };
910 $conf ->{ $k } = print_lxc_network
( $net );
917 # verify and cleanup nameserver list (replace \0 with ' ')
918 sub verify_nameserver_list
{
919 my ( $nameserver_list ) = @_ ;
922 foreach my $server ( PVE
:: Tools
:: split_list
( $nameserver_list )) {
923 PVE
:: JSONSchema
:: pve_verify_ip
( $server );
927 return join ( ' ' , @list );
930 sub verify_searchdomain_list
{
931 my ( $searchdomain_list ) = @_ ;
934 foreach my $server ( PVE
:: Tools
:: split_list
( $searchdomain_list )) {
935 # todo: should we add checks for valid dns domains?
939 return join ( ' ' , @list );
942 sub update_lxc_config
{
943 my ( $vmid, $conf, $running, $param, $delete ) = @_ ;
946 die "unable to modify config while container is running \n " if $running ;
948 if ( defined ( $delete )) {
949 foreach my $opt ( @$delete ) {
950 if ( $opt eq 'hostname' || $opt eq 'memory' ) {
951 die "unable to delete required option ' $opt ' \n " ;
952 } elsif ( $opt eq 'swap' ) {
953 delete $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' };
954 } elsif ( $opt eq 'description' ) {
955 delete $conf ->{ 'pve.comment' };
956 } elsif ( $opt eq 'onboot' ) {
957 delete $conf ->{ 'pve.onboot' };
958 } elsif ( $opt eq 'startup' ) {
959 delete $conf ->{ 'pve.startup' };
960 } elsif ( $opt eq 'nameserver' ) {
961 delete $conf ->{ 'pve.nameserver' };
962 } elsif ( $opt eq 'searchdomain' ) {
963 delete $conf ->{ 'pve.searchdomain' };
964 } elsif ( $opt =~ m/^net\d$/ ) {
965 delete $conf ->{ $opt };
972 foreach my $opt ( keys %$param ) {
973 my $value = $param ->{ $opt };
974 if ( $opt eq 'hostname' ) {
975 $conf ->{ 'lxc.utsname' } = $value ;
976 } elsif ( $opt eq 'onboot' ) {
977 $conf ->{ 'pve.onboot' } = $value ?
1 : 0 ;
978 } elsif ( $opt eq 'startup' ) {
979 $conf ->{ 'pve.startup' } = $value ;
980 } elsif ( $opt eq 'nameserver' ) {
981 my $list = verify_nameserver_list
( $value );
982 $conf ->{ 'pve.nameserver' } = $list ;
983 } elsif ( $opt eq 'searchdomain' ) {
984 my $list = verify_searchdomain_list
( $value );
985 $conf ->{ 'pve.searchdomain' } = $list ;
986 } elsif ( $opt eq 'memory' ) {
987 $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } = $value*1024*1024 ;
988 } elsif ( $opt eq 'swap' ) {
989 my $mem = $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' };
990 $mem = $param ->{ memory
}* 1024 * 1024 if $param ->{ memory
};
991 $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' } = $mem + $value*1024*1024 ;
992 } elsif ( $opt eq 'cpus' ) {
994 my $cfs_period_us = 100000 ;
995 $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' } = $cfs_period_us ;
996 $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' } = $cfs_period_us*$value ;
998 delete $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
999 delete $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
1001 } elsif ( $opt eq 'cpuunits' ) {
1002 $conf ->{ 'lxc.cgroup.cpu.shares' } = $value ;
1003 } elsif ( $opt eq 'description' ) {
1004 $conf ->{ 'pve.comment' } = PVE
:: Tools
:: encode_text
( $value );
1005 } elsif ( $opt eq 'disk' ) {
1006 $conf ->{ 'pve.disksize' } = $value ;
1007 } elsif ( $opt =~ m/^net(\d+)$/ ) {
1009 my $net = PVE
:: LXC
:: parse_lxc_network
( $value );
1010 $net ->{ 'veth.pair' } = "veth${vmid}. $netid " ;
1011 $conf ->{ $opt } = $net ;
1018 sub get_primary_ips
{
1021 # return data from net0
1023 my $net = $conf ->{ net0
};
1024 return undef if ! $net ;
1026 my $ipv4 = $net ->{ ip
};
1027 $ipv4 =~ s!/\d+$!! if $ipv4 ;
1028 my $ipv6 = $net ->{ ip
};
1029 $ipv6 =~ s!/\d+$!! if $ipv6 ;
1031 return ( $ipv4, $ipv6 );
1034 sub destory_lxc_container
{
1035 my ( $storage_cfg, $vmid, $conf ) = @_ ;
1037 if ( my $volid = $conf ->{ 'pve.volid' }) {
1039 my ( $vtype, $name, $owner ) = PVE
:: Storage
:: parse_volname
( $storage_cfg, $volid );
1040 die "got strange volid (containe is not owner!) \n " if $vmid != $owner ;
1042 PVE
:: Storage
:: vdisk_free
( $storage_cfg, $volid );
1044 destroy_config
( $vmid );
1047 my $cmd = [ 'lxc-destroy' , '-n' , $vmid ];
1049 PVE
:: Tools
:: run_command
( $cmd );