X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;ds=sidebyside;f=src%2FPVE%2FJSONSchema.pm;h=0d6860812ff4d37adccec866773942096d0116d9;hb=2f9e609a46a4dc7ebfcbaf8d65b7233ecb55bfe4;hp=cb2d56e1a08482f8b85016c9cde5b1e3738e6f07;hpb=638edfd4292f08edaabb78228a53a0fe8920358c;p=pve-common.git diff --git a/src/PVE/JSONSchema.pm b/src/PVE/JSONSchema.pm index cb2d56e..0d68608 100644 --- a/src/PVE/JSONSchema.pm +++ b/src/PVE/JSONSchema.pm @@ -227,26 +227,70 @@ sub pve_verify_ipv4mask { return $mask; } -register_format('CIDR', \&pve_verify_cidr); -sub pve_verify_cidr { +register_format('CIDRv6', \&pve_verify_cidrv6); +sub pve_verify_cidrv6 { my ($cidr, $noerr) = @_; - if ($cidr =~ m!^(?:$IPV4RE)(?:/(\d+))$! && ($1 > 7) && ($1 < 32)) { + if ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 120)) { return $cidr; - } elsif ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 120)) { + } + + return undef if $noerr; + die "value does not look like a valid IPv6 CIDR network\n"; +} + +register_format('CIDRv4', \&pve_verify_cidrv4); +sub pve_verify_cidrv4 { + my ($cidr, $noerr) = @_; + + if ($cidr =~ m!^(?:$IPV4RE)(?:/(\d+))$! && ($1 > 7) && ($1 < 32)) { return $cidr; } return undef if $noerr; - die "value does not look like a valid CIDR network\n"; + die "value does not look like a valid IPv4 CIDR network\n"; +} + +register_format('CIDR', \&pve_verify_cidr); +sub pve_verify_cidr { + my ($cidr, $noerr) = @_; + + if (!(pve_verify_cidrv4($cidr, 1) || + pve_verify_cidrv6($cidr, 1))) + { + return undef if $noerr; + die "value does not look like a valid CIDR network\n"; + } + + return $cidr; +} + +register_format('pve-ipv4-config', \&pve_verify_ipv4_config); +sub pve_verify_ipv4_config { + my ($config, $noerr) = @_; + + return $config if $config =~ /^(?:dhcp|manual)$/ || + pve_verify_cidrv4($config, 1); + return undef if $noerr; + die "value does not look like a valid ipv4 network configuration\n"; +} + +register_format('pve-ipv6-config', \&pve_verify_ipv6_config); +sub pve_verify_ipv6_config { + my ($config, $noerr) = @_; + + return $config if $config =~ /^(?:auto|dhcp|manual)$/ || + pve_verify_cidrv6($config, 1); + return undef if $noerr; + die "value does not look like a valid ipv6 network configuration\n"; } register_format('email', \&pve_verify_email); sub pve_verify_email { my ($email, $noerr) = @_; - # we use same regex as extjs Ext.form.VTypes.email - if ($email !~ /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/) { + # we use same regex as in Utils.js + if ($email !~ /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/) { return undef if $noerr; die "value does not look like a valid email address\n"; } @@ -352,8 +396,9 @@ PVE::JSONSchema::register_standard_option('pve-startup-order', { }); sub check_format { - my ($format, $value) = @_; + my ($format, $value, $path) = @_; + return parse_property_string($format, $value, $path) if ref($format) eq 'HASH'; return if $format eq 'regex'; if ($format =~ m/^(.*)-a?list$/) { @@ -383,10 +428,56 @@ sub check_format { die "undefined format '$format'\n" if !$code; + return parse_property_string($code, $value, $path) if ref($code) eq 'HASH'; &$code($value); } } +sub parse_property_string { + my ($format, $data, $path) = @_; + + my $default_key; + + my $res = {}; + foreach my $part (split(/,/, $data)) { + next if $part =~ /^\s*$/; + + if ($part =~ /^([^=]+)=(.+)$/) { + my ($k, $v) = ($1, $2); + die "duplicate key in comma-separated list property: $k" if defined($res->{$k}); + my $schema = $format->{$k}; + die "invalid key in comma-separated list property: $k" if !$schema; + if ($schema->{type} && $schema->{type} eq 'boolean') { + $v = 1 if $v =~ m/^(1|on|yes|true)$/i; + $v = 0 if $v =~ m/^(0|off|no|false)$/i; + } + $res->{$k} = $v; + } elsif ($part !~ /=/) { + die "duplicate key in comma-separated list property: $default_key" if $default_key; + foreach my $key (keys %$format) { + if ($format->{$key}->{default_key}) { + $default_key = $key; + if (!$res->{$default_key}) { + $res->{$default_key} = $part; + last; + } + die "duplicate key in comma-separated list property: $default_key"; + } + } + } else { + die "missing key in comma-separated list property"; + } + } + + my $errors = {}; + check_object($path, $format, $res, undef, $errors); + if (scalar(%$errors)) { + raise "format error", errors => $errors; + } + + return $res; +} + sub add_error { my ($errors, $path, $msg) = @_; @@ -563,6 +654,19 @@ sub check_object { } } +sub check_object_warn { + my ($path, $schema, $value, $additional_properties) = @_; + my $errors = {}; + check_object($path, $schema, $value, $additional_properties, $errors); + if (scalar(%$errors)) { + foreach my $k (keys %$errors) { + warn "parse error: $k: $errors->{$k}\n"; + } + return 0; + } + return 1; +} + sub check_prop { my ($value, $schema, $path, $errors) = @_; @@ -626,7 +730,7 @@ sub check_prop { } else { if (my $format = $schema->{format}) { - eval { check_format($format, $value); }; + eval { check_format($format, $value, $path); }; if ($@) { add_error($errors, $path, "invalid format - $@"); return; @@ -797,6 +901,11 @@ my $default_schema_noref = { optional => 1, description => "This provides a description of the purpose the instance property. The value can be a string or it can be an object with properties corresponding to various different instance languages (with an optional default property indicating the default description).", }, + format_description => { + type => "string", + optional => 1, + description => "This provides a shorter (usually just one word) description for a property used to generate descriptions for comma separated list property strings.", + }, title => { type => "string", optional => 1, @@ -808,10 +917,15 @@ my $default_schema_noref = { description => "indicates a required property or a schema that must be validated if this property is present", }, format => { - type => "string", + type => [ "string", "object" ], optional => 1, description => "This indicates what format the data is among some predefined formats which may include:\n\ndate - a string following the ISO format \naddress \nschema - a schema definition object \nperson \npage \nhtml - a string representing HTML", }, + default_key => { + type => "boolean", + optional => 1, + description => "Whether this is the default key in a comma separated list property string.", + }, default => { type => "any", optional => 1, @@ -1227,4 +1341,41 @@ sub dump_config { return $data; } +sub generate_typetext { + my ($schema) = @_; + my $typetext = ''; + my (@optional, @required); + foreach my $key (sort keys %$schema) { + next if !$schema->{$key}->{format_description} && + !$schema->{$key}->{typetext}; + if ($schema->{$key}->{optional}) { + push @optional, $key; + } else { + push @required, $key; + } + } + my ($pre, $post) = ('', ''); + my $add = sub { + my ($key) = @_; + if (my $desc = $schema->{$key}->{format_description}) { + $typetext .= "$pre$key=<$desc>$post"; + } elsif (my $text = $schema->{$key}->{typetext}) { + $typetext .= "$pre$text$post"; + } else { + die "internal error: neither format_description nor typetext found"; + } + }; + foreach my $key (@required) { + &$add($key); + $pre = ', '; + } + $pre = $pre ? ' [,' : '['; + $post = ']'; + foreach my $key (@optional) { + &$add($key); + $pre = ' [,'; + } + return $typetext; +} + 1;