]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/SectionConfig.pm
cgroup: cpu quota: fix resetting period length for v1
[pve-common.git] / src / PVE / SectionConfig.pm
index f6646f3398a1ec69e9e6c3ae1a71025641d4fbc4..af0af03d7beec66fea159b7191d26f9a04beeaf7 100644 (file)
@@ -2,12 +2,13 @@ package PVE::SectionConfig;
 
 use strict;
 use warnings;
+
+use Carp;
 use Digest::SHA;
+
 use PVE::Exception qw(raise_param_exc);
 use PVE::JSONSchema qw(get_standard_option);
 
-use Data::Dumper;
-
 my $defaultData = {
     options => {},
     plugins => {},
@@ -26,6 +27,9 @@ sub register {
     my $type = $class->type();
     my $pdata = $class->private();
 
+    die "duplicate plugin registration (type = $type)"
+       if defined($pdata->{plugins}->{$type});
+
     my $plugindata = $class->plugindata();
     $pdata->{plugindata}->{$type} = $plugindata;
     $pdata->{plugins}->{$type} = $class;
@@ -41,14 +45,14 @@ sub properties {
 
 sub options {
     return {};
-}   
+}
 
 sub plugindata {
     return {};
-}   
+}
 
 sub createSchema {
-    my ($class) = @_;
+    my ($class, $skip_type) = @_;
 
     my $pdata = $class->private();
     my $propertyList = $pdata->{propertyList};
@@ -68,7 +72,8 @@ sub createSchema {
     };
 
     foreach my $p (keys %$propertyList) {
-       next if $p eq 'type';
+       next if $skip_type && $p eq 'type';
+
        if (!$propertyList->{$p}->{optional}) {
            $props->{$p} = $propertyList->{$p};
            next;
@@ -102,7 +107,7 @@ sub createSchema {
 }
 
 sub updateSchema {
-    my ($class) = @_;
+    my ($class, $single_class) = @_;
 
     my $pdata = $class->private();
     my $propertyList = $pdata->{propertyList};
@@ -110,8 +115,15 @@ sub updateSchema {
 
     my $props = {};
 
+    my $filter_type = $class->type() if $single_class;
+
     foreach my $p (keys %$propertyList) {
        next if $p eq 'type';
+
+       my $copts = $class->options();
+
+       next if defined($filter_type) && !defined($copts->{$p});
+
        if (!$propertyList->{$p}->{optional}) {
            $props->{$p} = $propertyList->{$p};
            next;
@@ -119,6 +131,8 @@ sub updateSchema {
 
        my $modifyable = 0;
 
+       $modifyable = 1 if defined($copts->{$p}) && !$copts->{$p}->{fixed};
+
        foreach my $t (keys %$plugins) {
            my $opts = $pdata->{options}->{$t} || {};
            next if !defined($opts->{$p});
@@ -179,12 +193,14 @@ sub init {
     }
 
     $propertyList->{type}->{type} = 'string';
-    $propertyList->{type}->{enum} = [keys %$plugins];
+    $propertyList->{type}->{enum} = [sort keys %$plugins];
 }
 
 sub lookup {
     my ($class, $type) = @_;
 
+    croak "cannot lookup undefined type!" if !defined($type);
+
     my $pdata = $class->private();
     my $plugin = $pdata->{plugins}->{$type};
 
@@ -197,8 +213,8 @@ sub lookup_types {
     my ($class) = @_;
 
     my $pdata = $class->private();
-    
-    return [ keys %{$pdata->{plugins}} ];
+
+    return [ sort keys %{$pdata->{plugins}} ];
 }
 
 sub decode_value {
@@ -221,7 +237,7 @@ sub check_value {
     return $value if $key eq 'type' && $type eq $value;
 
     my $opts = $pdata->{options}->{$type};
-    die "unknown section type '$type'\n" if !$opts; 
+    die "unknown section type '$type'\n" if !$opts;
 
     die "unexpected property '$key'\n" if !defined($opts->{$key});
 
@@ -246,6 +262,10 @@ sub check_value {
        }
     }
 
+    if ($ct eq 'boolean' || $ct eq 'integer' || $ct eq 'number') {
+       return $value + 0; # convert to number
+    }
+
     return $value;
 }
 
@@ -262,7 +282,7 @@ sub parse_section_header {
 }
 
 sub format_section_header {
-    my ($class, $type, $sectionId) = @_;
+    my ($class, $type, $sectionId, $scfg, $done_hash) = @_;
 
     return "$type: $sectionId\n";
 }
@@ -279,19 +299,23 @@ sub parse_config {
     $raw = '' if !defined($raw);
 
     my $digest = Digest::SHA::sha1_hex($raw);
-    
+
     my $pri = 1;
 
     my $lineno = 0;
     my @lines = split(/\n/, $raw);
     my $nextline = sub {
-       while (my $line = shift @lines) {
+       while (defined(my $line = shift @lines)) {
            $lineno++;
-           return $line if $line !~ /^\s*(?:#|$)/;
+           return $line if ($line !~ /^\s*#/);
        }
     };
 
-    while (my $line = &$nextline()) {
+    my $errors = [];
+    while (@lines) {
+       my $line = $nextline->();
+       next if !$line;
+
        my $errprefix = "file $filename line $lineno";
 
        my ($type, $sectionId, $errmsg, $config) = $class->parse_section_header($line);
@@ -314,19 +338,27 @@ sub parse_config {
                }
            }
 
-           while ($line = &$nextline()) {
+           while ($line = $nextline->()) {
                next if $ignore; # skip
 
                $errprefix = "file $filename line $lineno";
 
                if ($line =~ m/^\s+(\S+)(\s+(.*\S))?\s*$/) {
                    my ($k, $v) = ($1, $3);
-   
+
                    eval {
                        die "duplicate attribute\n" if defined($config->{$k});
                        $config->{$k} = $plugin->check_value($type, $k, $v, $sectionId);
                    };
-                   warn "$errprefix (section '$sectionId') - unable to parse value of '$k': $@" if $@;
+                   if (my $err = $@) {
+                       warn "$errprefix (section '$sectionId') - unable to parse value of '$k': $err";
+                       push @$errors, {
+                           context => $errprefix,
+                           section => $sectionId,
+                           key => $k,
+                           err => $err,
+                       };
+                   }
 
                } else {
                    warn "$errprefix (section '$sectionId') - ignore config line: $line\n";
@@ -345,8 +377,12 @@ sub parse_config {
        }
     }
 
-
-    my $cfg = { ids => $ids, order => $order, digest => $digest};
+    my $cfg = {
+       ids => $ids,
+       order => $order,
+       digest => $digest
+    };
+    $cfg->{errors} = $errors if scalar(@$errors) > 0;
 
     return $cfg;
 }
@@ -362,10 +398,10 @@ sub check_config {
 
     foreach my $k (keys %$config) {
        my $value = $config->{$k};
-       
+
        die "can't change value of fixed parameter '$k'\n"
-           if !$create && $opts->{$k}->{fixed};
-       
+           if !$create && defined($opts->{$k}) && $opts->{$k}->{fixed};
+
        if (defined($value)) {
            my $tmp = $class->check_value($type, $k, $value, $sectionId, $skipSchemaCheck);
            $settings->{$k} = $class->decode_value($type, $k, $tmp);
@@ -391,8 +427,12 @@ my $format_config_line = sub {
 
     my $ct = $schema->{type};
 
+    die "property '$key' contains a line feed\n"
+       if ($key =~ m/[\n\r]/) || ($value =~ m/[\n\r]/);
+
     if ($ct eq 'boolean') {
-       return $value ? "\t$key\n" : '';
+       return "\t$key " . ($value ? 1 : 0) . "\n"
+           if defined($value);
     } else {
        return "\t$key $value\n" if "$value" ne '';
     }
@@ -411,13 +451,13 @@ sub write_config {
 
     my $maxpri = 0;
     foreach my $sectionId (keys %$ids) {
-       my $pri = $order->{$sectionId}; 
+       my $pri = $order->{$sectionId};
        $maxpri = $pri if $pri && $pri > $maxpri;
     }
     foreach my $sectionId (keys %$ids) {
        if (!defined ($order->{$sectionId})) {
            $order->{$sectionId} = ++$maxpri;
-       } 
+       }
     }
 
     foreach my $sectionId (sort {$order->{$a} <=> $order->{$b}} keys %$ids) {
@@ -427,18 +467,23 @@ sub write_config {
 
        die "unknown section type '$type'\n" if !$opts;
 
-       my $data = $class->format_section_header($type, $sectionId);
-       if ($scfg->{comment}) {
+       my $done_hash = {};
+
+       my $data = $class->format_section_header($type, $sectionId, $scfg, $done_hash);
+       if ($scfg->{comment} && !$done_hash->{comment}) {
            my $k = 'comment';
            my $v = $class->encode_value($type, $k, $scfg->{$k});
            $data .= &$format_config_line($propertyList->{$k}, $k, $v);
        }
 
-       $data .= "\tdisable\n" if $scfg->{disable};
+       $data .= "\tdisable\n" if $scfg->{disable} && !$done_hash->{disable};
 
-       my $done_hash = { comment => 1, disable => 1};
+       $done_hash->{comment} = 1;
+       $done_hash->{disable} = 1;
 
-       foreach my $k (keys %$opts) {
+       my @option_keys = sort keys %$opts;
+       foreach my $k (@option_keys) {
+           next if defined($done_hash->{$k});
            next if $opts->{$k}->{optional};
            $done_hash->{$k} = 1;
            my $v = $scfg->{$k};
@@ -448,7 +493,7 @@ sub write_config {
            $data .= &$format_config_line($propertyList->{$k}, $k, $v);
        }
 
-       foreach my $k (keys %$opts) {
+       foreach my $k (@option_keys) {
            next if defined($done_hash->{$k});
            my $v = $scfg->{$k};
            next if !defined($v);