]>
git.proxmox.com Git - pve-container.git/blob - src/pve-update-lxc-config
3 # update old beta1 config
4 # todo: remove this script after 4.0 Release
10 use PVE
:: JSONSchema
qw(get_standard_option) ;
14 my ( $volid, $noerr ) = @_ ;
16 if ( $volid =~ m/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):(.+)$/i ) {
17 return wantarray ?
( $1, $2 ) : $1 ;
19 return undef if $noerr ;
20 die "unable to parse volume ID ' $volid ' \n " ;
24 my ( $name, $value ) = @_ ;
26 if ( $value =~ m/^(\d+)(b|k|m|g)?$/i ) {
27 my ( $res, $unit ) = ( $1, lc ( $2 || 'b' ));
29 return $res if $unit eq 'b' ;
30 return $res*1024 if $unit eq 'k' ;
31 return $res*1024*1024 if $unit eq 'm' ;
32 return $res*1024*1024*1024 if $unit eq 'g' ;
38 sub verify_searchdomain_list
{
39 my ( $searchdomain_list ) = @_ ;
42 foreach my $server ( PVE
:: Tools
:: split_list
( $searchdomain_list )) {
43 # todo: should we add checks for valid dns domains?
47 return join ( ' ' , @list );
50 sub verify_nameserver_list
{
51 my ( $nameserver_list ) = @_ ;
54 foreach my $server ( PVE
:: Tools
:: split_list
( $nameserver_list )) {
55 PVE
:: JSONSchema
:: pve_verify_ip
( $server );
59 return join ( ' ' , @list );
62 my $valid_lxc_keys = {
63 'lxc.arch' => 'i386|x86|i686|x86_64|amd64' ,
71 'lxc.cgroup.memory.limit_in_bytes' => \
& parse_lxc_size
,
72 'lxc.cgroup.memory.memsw.limit_in_bytes' => \
& parse_lxc_size
,
73 'lxc.cgroup.cpu.cfs_period_us' => '\d+' ,
74 'lxc.cgroup.cpu.cfs_quota_us' => '\d+' ,
75 'lxc.cgroup.cpu.shares' => '\d+' ,
79 'lxc.mount.entry' => 1 ,
80 'lxc.mount.auto' => 1 ,
88 'lxc.haltsignal' => 1 ,
89 'lxc.rebootsignal' => 1 ,
90 'lxc.stopsignal' => 1 ,
93 'lxc.console.logfile' => 1 ,
99 'lxc.aa_profile' => 1 ,
100 'lxc.aa_allow_incomplete' => 1 ,
101 'lxc.se_context' => 1 ,
104 'lxc.environment' => 1 ,
105 'lxc.cgroup.devices.deny' => 1 ,
108 'lxc.start.auto' => 1 ,
109 'lxc.start.delay' => 1 ,
110 'lxc.start.order' => 1 ,
114 'lxc.hook.pre-start' => 1 ,
115 'lxc.hook.pre-mount' => 1 ,
116 'lxc.hook.mount' => 1 ,
117 'lxc.hook.autodev' => 1 ,
118 'lxc.hook.start' => 1 ,
119 'lxc.hook.post-stop' => 1 ,
120 'lxc.hook.clone' => 1 ,
123 'pve.nameserver' => sub {
124 my ( $name, $value ) = @_ ;
125 return verify_nameserver_list
( $value );
127 'pve.searchdomain' => sub {
128 my ( $name, $value ) = @_ ;
129 return verify_searchdomain_list
( $value );
131 'pve.onboot' => '(0|1)' ,
132 'pve.startup' => sub {
133 my ( $name, $value ) = @_ ;
134 return PVE
:: JSONSchema
:: pve_verify_startup_order
( $value );
137 'pve.disksize' => '\d+(\.\d+)?' ,
139 my ( $name, $value ) = @_ ;
140 parse_volume_id
( $value );
147 'pve.snapcomment' => 1 ,
149 'pve.snapstate' => 1 ,
153 my $valid_lxc_network_keys = {
156 name
=> 1 , # ifname inside container
157 'veth.pair' => 1 , # ifname at host (eth${vmid}.X)
161 my $valid_pve_network_keys = {
171 my $lxc_array_configs = {
176 'lxc.cgroup.devices.deny' => 1 ,
179 sub parse_lxc_option
{
180 my ( $name, $value ) = @_ ;
182 my $parser = $valid_lxc_keys ->{ $name };
184 die "invalid key ' $name ' \n " if ! defined ( $parser );
186 if ( $parser eq '1' ) {
188 } elsif ( ref ( $parser )) {
189 my $res = & $parser ( $name, $value );
190 return $res if defined ( $res );
193 return $value if $value =~ m/^$parser$/ ;
196 die "unable to parse value ' $value ' for option ' $name ' \n " ;
199 sub parse_lxc_config
{
200 my ( $filename, $raw ) = @_ ;
202 return undef if ! defined ( $raw );
205 #digest => Digest::SHA::sha1_hex($raw),
208 $filename =~ m
| /lxc/ ( \d
+)/ config
$|
209 || die "got strange filename ' $filename '" ;
211 # Note: restore pass filename "lxc/0/config"
214 my $check_net_vmid = sub {
217 die "wrong vmid for network interface pair \n " if $vmid != $netvmid ;
220 my $network_counter = 0 ;
221 my $network_list = [];
222 my $host_ifnames = {};
226 my $push_network = sub {
229 push @{ $network_list }, $netconf ;
231 if ( my $netname = $netconf ->{ 'veth.pair' }) {
232 if ( $netname =~ m/^veth(\d+).(\d)$/ ) {
233 & $check_net_vmid ( $1 );
234 $host_ifnames ->{ $netname } = 1 ;
236 die "wrong network interface pair \n " ;
241 my $finalize_section = sub {
242 & $push_network ( $network ); # flush
244 foreach my $net (@{ $network_list }) {
245 next if $net ->{ type
} eq 'empty' ; # skip
246 if (! $net ->{ hwaddr
}) {
247 my $dc = PVE
:: Cluster
:: cfs_read_file
( 'datacenter.cfg' );
248 $net ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
( $dc ->{ mac_prefix
});
250 die "unsupported network type ' $net ->{type}' \n " if $net ->{ type
} ne 'veth' ;
251 die "undefined veth network pair' \n " if ! $net ->{ 'veth.pair' };
253 if ( $net ->{ 'veth.pair' } =~ m/^veth\d+.(\d+)$/ ) {
255 $data ->{ snapshots
}->{ $snapname }->{ "net $1 " } = $net ;
257 $data ->{ "net $1 " } = $net ;
263 $network_counter = 0 ;
269 while ( $raw && $raw =~ s/^(.*)?(\n|$)// ) {
271 next if $line =~ m/^\s*$/ ; # skip empty lines
272 next if $line =~ m/^#/ ; # skip comments
274 # snap.pve.snapname starts new sections
275 if ( $line =~ m/^(snap\.)?pve\.snapname\s*=\s*(\w*)\s*$/ ) {
278 & $finalize_section ();
281 $data ->{ snapshots
}->{ $snapname }->{ 'pve.snapname' } = $snapname ;
283 } elsif ( $line =~ m/^(snap\.)?lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
284 my ( $subkey, $value ) = ( $2, $3 );
285 if ( $subkey eq 'type' ) {
286 & $push_network ( $network );
287 $network = { type
=> $value };
288 } elsif ( $valid_lxc_network_keys ->{ $subkey }) {
289 $network ->{ $subkey } = $value ;
291 die "unable to parse config line: $line\n " ;
293 } elsif ( $line =~ m/^(snap\.)?pve\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
294 my ( $subkey, $value ) = ( $2, $3 );
295 if ( $valid_pve_network_keys ->{ $subkey }) {
296 $network ->{ $subkey } = $value ;
298 die "unable to parse config line: $line\n " ;
300 } elsif ( $line =~ m/^(snap\.)?((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/ ) {
301 my ( $name, $value ) = ( $2, $3 );
303 if ( $lxc_array_configs ->{ $name }) {
304 $data ->{ $name } = [] if ! defined ( $data ->{ $name });
306 push @{ $data ->{ snapshots
}->{ $snapname }->{ $name }}, parse_lxc_option
( $name, $value );
308 push @{ $data ->{ $name }}, parse_lxc_option
( $name, $value );
312 die "multiple definitions for $name\n " if defined ( $data ->{ snapshots
}->{ $snapname }->{ $name });
313 $data ->{ snapshots
}->{ $snapname }->{ $name } = parse_lxc_option
( $name, $value );
315 die "multiple definitions for $name\n " if defined ( $data ->{ $name });
316 $data ->{ $name } = parse_lxc_option
( $name, $value );
320 die "unable to parse config line: $line\n " ;
324 & $finalize_section ();
329 # Same confdesc entries are unchanged
330 # Deleted ones have `deprecated => 1`
331 # New ones `new => 1`
332 # New required ones `required => 1`
337 description
=> "Specifies whether a VM will be started during system bootup." ,
340 startup
=> get_standard_option
( 'pve-startup-order' ),
344 enum
=> [ 'amd64' , 'i386' ],
345 description
=> "OS architecture type." ,
353 enum
=> [ 'debian' , 'ubuntu' , 'centos' ],
354 description
=> "OS type. Corresponds to lxc setup scripts in /usr/share/lxc/config/<ostype>.common.conf." ,
361 description
=> "Specify the number of tty available to the container" ,
370 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." ,
378 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." ,
386 description
=> "Amount of RAM for the VM in MB." ,
393 description
=> "Amount of SWAP for the VM in MB." ,
400 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
407 description
=> "Set a host name for the container." ,
414 description
=> "Container description. Only used on the configuration web interface." ,
419 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
424 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." ,
426 rootfs
=> { #get_standard_option('pve-ct-rootfs'),
427 type
=> 'string' , format
=> 'pve-ct-mountpoint' ,
428 typetext
=> '[volume=]volume,] [,backup=yes|no] [,size=\d+]' ,
429 description
=> "Use volume as container root." ,
436 # type => 'string', format => 'pve-configid',
438 # description => "Parent snapshot name. This is used internally, and should not be modified.",
443 # description => "Timestamp for snapshots.",
450 my $MAX_LXC_NETWORKS = 10 ;
451 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
452 $confdesc ->{ "net $i " } = {
454 type
=> 'string' , format
=> 'pve-lxc-network' ,
455 description
=> "Specifies network interfaces for the container. \n\n " .
456 "The string should have the follow format: \n\n " .
457 "-net<[0-9]> bridge=<vmbr<Nummber>>[,hwaddr=<MAC>] \n " .
458 "[,mtu=<Number>][,name=<String>][,ip=<IPv4Format/CIDR>] \n " .
459 ",ip6=<IPv6Format/CIDR>][,gw=<GatwayIPv4>] \n " .
460 ",gw6=<GatwayIPv6>][,firewall=<[1|0]>][,tag=<VlanNo>]" ,
464 sub json_config_properties
{
467 foreach my $opt ( keys %$confdesc ) {
468 $prop ->{ $opt } = $confdesc ->{ $opt };
474 sub print_lxc_network
{
477 die "no network name defined \n " if ! $net ->{ name
};
479 my $res = "name= $net ->{name}" ;
481 foreach my $k ( qw(hwaddr mtu bridge ip gw ip6 gw6 firewall tag) ) {
482 next if ! defined ( $net ->{ $k });
483 $res .= ", $k = $net ->{ $k }" ;
489 sub lxc_conf_to_pve
{
490 my ( $vmid, $lxc_conf ) = @_ ;
492 my $properties = json_config_properties
();
494 my $conf = {}; # digest => $lxc_conf->{digest} };
496 foreach my $k ( keys %$properties ) {
498 if ( $k eq 'description' ) {
499 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
500 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
502 } elsif ( $k eq 'onboot' ) {
503 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
504 } elsif ( $k eq 'startup' ) {
505 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
506 } elsif ( $k eq 'arch' ) {
507 $conf ->{ $k } = $lxc_conf ->{ 'lxc.arch' } if $lxc_conf ->{ 'lxc.arch' };
508 } elsif ( $k eq 'ostype' ) {
509 foreach (@{ $lxc_conf ->{ 'lxc.include' }}) {
510 if ( m
@^\s*/usr/share/lxc/config/ (.*) \
. common\
. conf\s
* $@ ) {
514 } elsif ( $k eq 'tty' ) {
515 $conf ->{ $k } = $lxc_conf ->{ 'lxc.tty' } if $lxc_conf ->{ 'lxc.tty' };
516 } elsif ( $k eq 'rootfs' ) {
517 $conf ->{ $k } = $lxc_conf ->{ 'pve.volid' } if $lxc_conf ->{ 'pve.volid' };
518 } elsif ( $k eq 'hostname' ) {
519 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
520 } elsif ( $k eq 'nameserver' ) {
521 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
522 } elsif ( $k eq 'searchdomain' ) {
523 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
524 } elsif ( $k eq 'memory' ) {
525 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
526 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
528 } elsif ( $k eq 'swap' ) {
529 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
530 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
531 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
533 } elsif ( $k eq 'cpulimit' ) {
534 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
535 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
537 if ( $cfs_period_us && $cfs_quota_us ) {
538 $conf ->{ $k } = $cfs_quota_us/$cfs_period_us ;
542 } elsif ( $k eq 'cpuunits' ) {
543 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
544 } elsif ( $k eq 'disk' ) {
545 $conf ->{ $k } = defined ( $lxc_conf ->{ 'pve.disksize' }) ?
546 $lxc_conf ->{ 'pve.disksize' } : 0 ;
547 } elsif ( $k =~ m/^net\d$/ ) {
548 my $net = $lxc_conf ->{ $k };
550 $conf ->{ $k } = print_lxc_network
( $net );
554 if ( my $parent = $lxc_conf ->{ 'pve.parent' }) {
555 $conf ->{ parent
} = $lxc_conf ->{ 'pve.parent' };
558 if ( my $parent = $lxc_conf ->{ 'pve.snapcomment' }) {
559 $conf ->{ description
} = $lxc_conf ->{ 'pve.snapcomment' };
562 if ( my $parent = $lxc_conf ->{ 'pve.snaptime' }) {
563 $conf ->{ snaptime
} = $lxc_conf ->{ 'pve.snaptime' };
571 my $out = "/etc/pve/lxc/${vmid}.conf" ;
572 my $old = "/etc/pve/lxc/${vmid}/config" ;
576 print "converting $old to $out\n " ;
578 open my $fh, '<' , $old or die "failed to open config $old : $! " ;
579 my $raw = do { local $/ ; < $fh > };
582 my $data = parse_lxc_config
( $old, $raw );
583 my $new = lxc_conf_to_pve
( $vmid, $data );
585 delete $new ->{ $_ } for grep { $confdesc ->{ $_ }->{ deprecated
} } keys %$new ;
587 foreach my $required ( grep { $confdesc ->{ $_ }->{ required
} } keys %$confdesc ) {
588 if (! $new ->{ $required }) {
589 die "failed to create required config key ' $required '" ;
593 open $fh, '>' , $out or die "failed to open output file $out : $! " ;
594 print { $fh } " $_ : $new ->{ $_ } \n " for sort keys %$new ;
598 chdir '/etc/pve/lxc' or die "failed to change directory to /etc/pve/lxc: $! " ;
600 next if ! /^\d+$/ || ! - d
$_ ;
601 eval { convert
( $_ ); };