]>
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 $net ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $net ->{ hwaddr
};
247 die "unsupported network type ' $net ->{type}' \n " if $net ->{ type
} ne 'veth' ;
248 die "undefined veth network pair' \n " if ! $net ->{ 'veth.pair' };
250 if ( $net ->{ 'veth.pair' } =~ m/^veth\d+.(\d+)$/ ) {
252 $data ->{ snapshots
}->{ $snapname }->{ "net $1 " } = $net ;
254 $data ->{ "net $1 " } = $net ;
260 $network_counter = 0 ;
266 while ( $raw && $raw =~ s/^(.*)?(\n|$)// ) {
268 next if $line =~ m/^\s*$/ ; # skip empty lines
269 next if $line =~ m/^#/ ; # skip comments
271 # snap.pve.snapname starts new sections
272 if ( $line =~ m/^(snap\.)?pve\.snapname\s*=\s*(\w*)\s*$/ ) {
275 & $finalize_section ();
278 $data ->{ snapshots
}->{ $snapname }->{ 'pve.snapname' } = $snapname ;
280 } elsif ( $line =~ m/^(snap\.)?lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
281 my ( $subkey, $value ) = ( $2, $3 );
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 " ;
290 } elsif ( $line =~ m/^(snap\.)?pve\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
291 my ( $subkey, $value ) = ( $2, $3 );
292 if ( $valid_pve_network_keys ->{ $subkey }) {
293 $network ->{ $subkey } = $value ;
295 die "unable to parse config line: $line\n " ;
297 } elsif ( $line =~ m/^(snap\.)?((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/ ) {
298 my ( $name, $value ) = ( $2, $3 );
300 if ( $lxc_array_configs ->{ $name }) {
301 $data ->{ $name } = [] if ! defined ( $data ->{ $name });
303 push @{ $data ->{ snapshots
}->{ $snapname }->{ $name }}, parse_lxc_option
( $name, $value );
305 push @{ $data ->{ $name }}, parse_lxc_option
( $name, $value );
309 die "multiple definitions for $name\n " if defined ( $data ->{ snapshots
}->{ $snapname }->{ $name });
310 $data ->{ snapshots
}->{ $snapname }->{ $name } = parse_lxc_option
( $name, $value );
312 die "multiple definitions for $name\n " if defined ( $data ->{ $name });
313 $data ->{ $name } = parse_lxc_option
( $name, $value );
317 die "unable to parse config line: $line\n " ;
321 & $finalize_section ();
326 # Same confdesc entries are unchanged
327 # Deleted ones have `deprecated => 1`
328 # New ones `new => 1`
329 # New required ones `required => 1`
334 description
=> "Specifies whether a VM will be started during system bootup." ,
337 startup
=> get_standard_option
( 'pve-startup-order' ),
341 enum
=> [ 'amd64' , 'i386' ],
342 description
=> "OS architecture type." ,
350 enum
=> [ 'debian' , 'ubuntu' , 'centos' ],
351 description
=> "OS type. Corresponds to lxc setup scripts in /usr/share/lxc/config/<ostype>.common.conf." ,
358 description
=> "Specify the number of tty available to the container" ,
367 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." ,
375 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." ,
383 description
=> "Amount of RAM for the VM in MB." ,
390 description
=> "Amount of SWAP for the VM in MB." ,
397 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
404 description
=> "Set a host name for the container." ,
411 description
=> "Container description. Only used on the configuration web interface." ,
416 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
421 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." ,
423 rootfs
=> { #get_standard_option('pve-ct-rootfs'),
424 type
=> 'string' , format
=> 'pve-ct-mountpoint' ,
425 typetext
=> '[volume=]volume,] [,backup=yes|no] [,size=\d+]' ,
426 description
=> "Use volume as container root." ,
433 # type => 'string', format => 'pve-configid',
435 # description => "Parent snapshot name. This is used internally, and should not be modified.",
440 # description => "Timestamp for snapshots.",
447 my $MAX_LXC_NETWORKS = 10 ;
448 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
449 $confdesc ->{ "net $i " } = {
451 type
=> 'string' , format
=> 'pve-lxc-network' ,
452 description
=> "Specifies network interfaces for the container. \n\n " .
453 "The string should have the follow format: \n\n " .
454 "-net<[0-9]> bridge=<vmbr<Nummber>>[,hwaddr=<MAC>] \n " .
455 "[,mtu=<Number>][,name=<String>][,ip=<IPv4Format/CIDR>] \n " .
456 ",ip6=<IPv6Format/CIDR>][,gw=<GatwayIPv4>] \n " .
457 ",gw6=<GatwayIPv6>][,firewall=<[1|0]>][,tag=<VlanNo>]" ,
461 sub json_config_properties
{
464 foreach my $opt ( keys %$confdesc ) {
465 $prop ->{ $opt } = $confdesc ->{ $opt };
471 sub print_lxc_network
{
474 die "no network name defined \n " if ! $net ->{ name
};
476 my $res = "name= $net ->{name}" ;
478 foreach my $k ( qw(hwaddr mtu bridge ip gw ip6 gw6 firewall tag) ) {
479 next if ! defined ( $net ->{ $k });
480 $res .= ", $k = $net ->{ $k }" ;
486 sub lxc_conf_to_pve
{
487 my ( $vmid, $lxc_conf ) = @_ ;
489 my $properties = json_config_properties
();
491 my $conf = {}; # digest => $lxc_conf->{digest} };
493 foreach my $k ( keys %$properties ) {
495 if ( $k eq 'description' ) {
496 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
497 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
499 } elsif ( $k eq 'onboot' ) {
500 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
501 } elsif ( $k eq 'startup' ) {
502 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
503 } elsif ( $k eq 'arch' ) {
504 $conf ->{ $k } = $lxc_conf ->{ 'lxc.arch' } if $lxc_conf ->{ 'lxc.arch' };
505 } elsif ( $k eq 'ostype' ) {
506 foreach (@{ $lxc_conf ->{ 'lxc.include' }}) {
507 if ( m
@^\s*/usr/share/lxc/config/ (.*) \
. common\
. conf\s
* $@ ) {
511 } elsif ( $k eq 'tty' ) {
512 $conf ->{ $k } = $lxc_conf ->{ 'lxc.tty' } if $lxc_conf ->{ 'lxc.tty' };
513 } elsif ( $k eq 'rootfs' ) {
514 $conf ->{ $k } = $lxc_conf ->{ 'pve.volid' } if $lxc_conf ->{ 'pve.volid' };
515 } elsif ( $k eq 'hostname' ) {
516 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
517 } elsif ( $k eq 'nameserver' ) {
518 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
519 } elsif ( $k eq 'searchdomain' ) {
520 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
521 } elsif ( $k eq 'memory' ) {
522 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
523 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
525 } elsif ( $k eq 'swap' ) {
526 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
527 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
528 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
530 } elsif ( $k eq 'cpulimit' ) {
531 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
532 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
534 if ( $cfs_period_us && $cfs_quota_us ) {
535 $conf ->{ $k } = $cfs_quota_us/$cfs_period_us ;
539 } elsif ( $k eq 'cpuunits' ) {
540 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
541 } elsif ( $k eq 'disk' ) {
542 $conf ->{ $k } = defined ( $lxc_conf ->{ 'pve.disksize' }) ?
543 $lxc_conf ->{ 'pve.disksize' } : 0 ;
544 } elsif ( $k =~ m/^net\d$/ ) {
545 my $net = $lxc_conf ->{ $k };
547 $conf ->{ $k } = print_lxc_network
( $net );
551 if ( my $parent = $lxc_conf ->{ 'pve.parent' }) {
552 $conf ->{ parent
} = $lxc_conf ->{ 'pve.parent' };
555 if ( my $parent = $lxc_conf ->{ 'pve.snapcomment' }) {
556 $conf ->{ description
} = $lxc_conf ->{ 'pve.snapcomment' };
559 if ( my $parent = $lxc_conf ->{ 'pve.snaptime' }) {
560 $conf ->{ snaptime
} = $lxc_conf ->{ 'pve.snaptime' };
568 my $out = "/etc/pve/lxc/${vmid}.conf" ;
569 my $old = "/etc/pve/lxc/${vmid}/config" ;
573 print "converting $old to $out\n " ;
575 open my $fh, '<' , $old or die "failed to open config $old : $! " ;
576 my $raw = do { local $/ ; < $fh > };
579 my $data = parse_lxc_config
( $old, $raw );
580 my $new = lxc_conf_to_pve
( $vmid, $data );
582 delete $new ->{ $_ } for grep { $confdesc ->{ $_ }->{ deprecated
} } keys %$new ;
584 foreach my $required ( grep { $confdesc ->{ $_ }->{ required
} } keys %$confdesc ) {
585 if (! $new ->{ $required }) {
586 die "failed to create required config key ' $required '" ;
590 open $fh, '>' , $out or die "failed to open output file $out : $! " ;
591 print { $fh } " $_ : $new ->{ $_ } \n " for sort keys %$new ;
595 chdir '/etc/pve/lxc' or die "failed to change directory to /etc/pve/lxc: $! " ;
597 next if ! /^\d+$/ || ! - d
$_ ;
598 eval { convert
( $_ ); };