]>
git.proxmox.com Git - pve-client.git/blob - PVE/APIClient/SectionConfig.pm
1 package PVE
::APIClient
::SectionConfig
;
6 use PVE
::APIClient
::JSONSchema
qw(get_standard_option);
25 my $type = $class->type();
26 my $pdata = $class->private();
28 die "duplicate plugin registration (type = $type)"
29 if defined($pdata->{plugins
}->{$type});
31 my $plugindata = $class->plugindata();
32 $pdata->{plugindata
}->{$type} = $plugindata;
33 $pdata->{plugins
}->{$type} = $class;
53 my ($class, $skip_type) = @_;
55 my $pdata = $class->private();
56 my $propertyList = $pdata->{propertyList
};
57 my $plugins = $pdata->{plugins
};
61 my $copy_property = sub {
65 foreach my $k (keys %$src) {
66 $res->{$k} = $src->{$k};
72 foreach my $p (keys %$propertyList) {
73 next if $skip_type && $p eq 'type';
75 if (!$propertyList->{$p}->{optional
}) {
76 $props->{$p} = $propertyList->{$p};
82 my $copts = $class->options();
83 $required = 0 if defined($copts->{$p}) && $copts->{$p}->{optional
};
85 foreach my $t (keys %$plugins) {
86 my $opts = $pdata->{options
}->{$t} || {};
87 $required = 0 if !defined($opts->{$p}) || $opts->{$p}->{optional
};
91 # make a copy, because we modify the optional property
92 my $res = &$copy_property($propertyList->{$p});
96 $props->{$p} = $propertyList->{$p};
102 additionalProperties
=> 0,
103 properties
=> $props,
108 my ($class, $single_class) = @_;
110 my $pdata = $class->private();
111 my $propertyList = $pdata->{propertyList
};
112 my $plugins = $pdata->{plugins
};
116 my $filter_type = $class->type() if $single_class;
118 foreach my $p (keys %$propertyList) {
119 next if $p eq 'type';
121 my $copts = $class->options();
123 next if defined($filter_type) && !defined($copts->{$p});
125 if (!$propertyList->{$p}->{optional
}) {
126 $props->{$p} = $propertyList->{$p};
132 $modifyable = 1 if defined($copts->{$p}) && !$copts->{$p}->{fixed
};
134 foreach my $t (keys %$plugins) {
135 my $opts = $pdata->{options
}->{$t} || {};
136 next if !defined($opts->{$p});
137 $modifyable = 1 if !$opts->{$p}->{fixed
};
139 next if !$modifyable;
141 $props->{$p} = $propertyList->{$p};
144 $props->{digest
} = get_standard_option
('pve-config-digest');
147 type
=> 'string', format
=> 'pve-configid-list',
148 description
=> "A list of settings you want to delete.",
155 additionalProperties
=> 0,
156 properties
=> $props,
163 my $pdata = $class->private();
165 foreach my $k (qw(options plugins plugindata propertyList)) {
166 $pdata->{$k} = {} if !$pdata->{$k};
169 my $plugins = $pdata->{plugins
};
170 my $propertyList = $pdata->{propertyList
};
172 foreach my $type (keys %$plugins) {
173 my $props = $plugins->{$type}->properties();
174 foreach my $p (keys %$props) {
175 die "duplicate property '$p'" if defined($propertyList->{$p});
176 my $res = $propertyList->{$p} = {};
177 my $data = $props->{$p};
178 for my $a (keys %$data) {
179 $res->{$a} = $data->{$a};
181 $res->{optional
} = 1;
185 foreach my $type (keys %$plugins) {
186 my $opts = $plugins->{$type}->options();
187 foreach my $p (keys %$opts) {
188 die "undefined property '$p'" if !$propertyList->{$p};
190 $pdata->{options
}->{$type} = $opts;
193 $propertyList->{type
}->{type
} = 'string';
194 $propertyList->{type
}->{enum
} = [sort keys %$plugins];
198 my ($class, $type) = @_;
200 my $pdata = $class->private();
201 my $plugin = $pdata->{plugins
}->{$type};
203 die "unknown section type '$type'\n" if !$plugin;
211 my $pdata = $class->private();
213 return [ sort keys %{$pdata->{plugins
}} ];
217 my ($class, $type, $key, $value) = @_;
223 my ($class, $type, $key, $value) = @_;
229 my ($class, $type, $key, $value, $storeid, $skipSchemaCheck) = @_;
231 my $pdata = $class->private();
233 return $value if $key eq 'type' && $type eq $value;
235 my $opts = $pdata->{options
}->{$type};
236 die "unknown section type '$type'\n" if !$opts;
238 die "unexpected property '$key'\n" if !defined($opts->{$key});
240 my $schema = $pdata->{propertyList
}->{$key};
241 die "unknown property type\n" if !$schema;
243 my $ct = $schema->{type
};
245 $value = 1 if $ct eq 'boolean' && !defined($value);
247 die "got undefined value\n" if !defined($value);
249 die "property contains a line feed\n" if $value =~ m/[\n\r]/;
251 if (!$skipSchemaCheck) {
253 PVE
::APIClient
::JSONSchema
::check_prop
($value, $schema, '', $errors);
254 if (scalar(keys %$errors)) {
255 die "$errors->{$key}\n" if $errors->{$key};
256 die "$errors->{_root}\n" if $errors->{_root
};
257 die "unknown error\n";
261 if ($ct eq 'boolean' || $ct eq 'integer' || $ct eq 'number') {
262 return $value + 0; # convert to number
268 sub parse_section_header
{
269 my ($class, $line) = @_;
271 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
272 my ($type, $sectionId) = ($1, $2);
273 my $errmsg = undef; # set if you want to skip whole section
274 my $config = {}; # to return additional attributes
275 return ($type, $sectionId, $errmsg, $config);
280 sub format_section_header
{
281 my ($class, $type, $sectionId, $scfg, $done_hash) = @_;
283 return "$type: $sectionId\n";
288 my ($class, $filename, $raw) = @_;
290 my $pdata = $class->private();
295 $raw = '' if !defined($raw);
297 my $digest = Digest
::SHA
::sha1_hex
($raw);
302 my @lines = split(/\n/, $raw);
304 while (my $line = shift @lines) {
306 return $line if $line !~ /^\s*(?:#|$)/;
310 while (my $line = &$nextline()) {
311 my $errprefix = "file $filename line $lineno";
313 my ($type, $sectionId, $errmsg, $config) = $class->parse_section_header($line);
322 warn "$errprefix (skip section '$sectionId'): $errmsg\n";
325 warn "$errprefix (skip section '$sectionId'): missing type - internal error\n";
327 if (!($plugin = $pdata->{plugins
}->{$type})) {
329 warn "$errprefix (skip section '$sectionId'): unsupported type '$type'\n";
333 while ($line = &$nextline()) {
334 next if $ignore; # skip
336 $errprefix = "file $filename line $lineno";
338 if ($line =~ m/^\s+(\S+)(\s+(.*\S))?\s*$/) {
339 my ($k, $v) = ($1, $3);
342 die "duplicate attribute\n" if defined($config->{$k});
343 $config->{$k} = $plugin->check_value($type, $k, $v, $sectionId);
345 warn "$errprefix (section '$sectionId') - unable to parse value of '$k': $@" if $@;
348 warn "$errprefix (section '$sectionId') - ignore config line: $line\n";
352 if (!$ignore && $type && $plugin && $config) {
353 $config->{type
} = $type;
354 eval { $ids->{$sectionId} = $plugin->check_config($sectionId, $config, 1, 1); };
355 warn "$errprefix (skip section '$sectionId'): $@" if $@;
356 $order->{$sectionId} = $pri++;
360 warn "$errprefix - ignore config line: $line\n";
365 my $cfg = { ids
=> $ids, order
=> $order, digest
=> $digest};
371 my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_;
373 my $type = $class->type();
374 my $pdata = $class->private();
375 my $opts = $pdata->{options
}->{$type};
377 my $settings = { type
=> $type };
379 foreach my $k (keys %$config) {
380 my $value = $config->{$k};
382 die "can't change value of fixed parameter '$k'\n"
383 if !$create && $opts->{$k}->{fixed
};
385 if (defined($value)) {
386 my $tmp = $class->check_value($type, $k, $value, $sectionId, $skipSchemaCheck);
387 $settings->{$k} = $class->decode_value($type, $k, $tmp);
389 die "got undefined value for option '$k'\n";
394 # check if we have a value for all required options
395 foreach my $k (keys %$opts) {
396 next if $opts->{$k}->{optional
};
397 die "missing value for required option '$k'\n"
398 if !defined($config->{$k});
405 my $format_config_line = sub {
406 my ($schema, $key, $value) = @_;
408 my $ct = $schema->{type
};
410 die "property '$key' contains a line feed\n"
411 if ($key =~ m/[\n\r]/) || ($value =~ m/[\n\r]/);
413 if ($ct eq 'boolean') {
414 return "\t$key " . ($value ?
1 : 0) . "\n"
417 return "\t$key $value\n" if "$value" ne '';
422 my ($class, $filename, $cfg) = @_;
424 my $pdata = $class->private();
425 my $propertyList = $pdata->{propertyList
};
429 my $ids = $cfg->{ids
};
430 my $order = $cfg->{order
};
433 foreach my $sectionId (keys %$ids) {
434 my $pri = $order->{$sectionId};
435 $maxpri = $pri if $pri && $pri > $maxpri;
437 foreach my $sectionId (keys %$ids) {
438 if (!defined ($order->{$sectionId})) {
439 $order->{$sectionId} = ++$maxpri;
443 foreach my $sectionId (sort {$order->{$a} <=> $order->{$b}} keys %$ids) {
444 my $scfg = $ids->{$sectionId};
445 my $type = $scfg->{type
};
446 my $opts = $pdata->{options
}->{$type};
448 die "unknown section type '$type'\n" if !$opts;
452 my $data = $class->format_section_header($type, $sectionId, $scfg, $done_hash);
453 if ($scfg->{comment
} && !$done_hash->{comment
}) {
455 my $v = $class->encode_value($type, $k, $scfg->{$k});
456 $data .= &$format_config_line($propertyList->{$k}, $k, $v);
459 $data .= "\tdisable\n" if $scfg->{disable
} && !$done_hash->{disable
};
461 $done_hash->{comment
} = 1;
462 $done_hash->{disable
} = 1;
464 my @option_keys = sort keys %$opts;
465 foreach my $k (@option_keys) {
466 next if defined($done_hash->{$k});
467 next if $opts->{$k}->{optional
};
468 $done_hash->{$k} = 1;
470 die "section '$sectionId' - missing value for required option '$k'\n"
472 $v = $class->encode_value($type, $k, $v);
473 $data .= &$format_config_line($propertyList->{$k}, $k, $v);
476 foreach my $k (@option_keys) {
477 next if defined($done_hash->{$k});
479 next if !defined($v);
480 $v = $class->encode_value($type, $k, $v);
481 $data .= &$format_config_line($propertyList->{$k}, $k, $v);
490 sub assert_if_modified
{
491 my ($cfg, $digest) = @_;
493 PVE
::APIClient
::Tools
::assert_if_modified
($cfg->{digest
}, $digest);