X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FJSONSchema.pm;h=aa82167346b052ecbff30596d89bf92e382e3ce0;hp=02aa2ab1d0228644b3929ad2541998e836ae4606;hb=ac15655f452fbcdfa44ffdd83822bbe353cd1767;hpb=b6501c2fd913d9359a48007594d94a8c8a8745b5 diff --git a/src/PVE/JSONSchema.pm b/src/PVE/JSONSchema.pm index 02aa2ab..aa82167 100644 --- a/src/PVE/JSONSchema.pm +++ b/src/PVE/JSONSchema.pm @@ -99,6 +99,20 @@ register_standard_option('extra-args', { optional => 1 }); +register_standard_option('fingerprint-sha256', { + description => "Certificate SHA 256 fingerprint.", + type => 'string', + pattern => '([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}', +}); + +register_standard_option('pve-output-format', { + type => 'string', + description => 'Output format.', + enum => [ 'text', 'plain', 'json' ], + optional => 1, + default => 'text', +}); + my $format_list = {}; sub register_format { @@ -115,6 +129,22 @@ sub get_format { return $format_list->{$format}; } +my $renderer_hash = {}; + +sub register_renderer { + my ($name, $code) = @_; + + die "renderer '$name' already registered\n" + if $renderer_hash->{$name}; + + $renderer_hash->{$name} = $code; +} + +sub get_renderer { + my ($name) = @_; + return $renderer_hash->{$name}; +} + # register some common type for pve register_format('string', sub {}); # allow format => 'string-list' @@ -257,7 +287,7 @@ register_format('CIDRv6', \&pve_verify_cidrv6); sub pve_verify_cidrv6 { my ($cidr, $noerr) = @_; - if ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 120)) { + if ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 128)) { return $cidr; } @@ -400,6 +430,42 @@ sub pve_verify_startup_order { die "unable to parse startup options\n"; } +my %bwlimit_opt = ( + optional => 1, + type => 'number', minimum => '0', + format_description => 'LIMIT', +); + +my $bwlimit_format = { + default => { + %bwlimit_opt, + description => 'default bandwidth limit in MiB/s', + }, + restore => { + %bwlimit_opt, + description => 'bandwidth limit in MiB/s for restoring guests from backups', + }, + migration => { + %bwlimit_opt, + description => 'bandwidth limit in MiB/s for migrating guests', + }, + clone => { + %bwlimit_opt, + description => 'bandwidth limit in MiB/s for cloning disks', + }, + move => { + %bwlimit_opt, + description => 'bandwidth limit in MiB/s for moving disks', + }, +}; +register_format('bwlimit', $bwlimit_format); +register_standard_option('bwlimit', { + description => "Set bandwidth/io limits various operations.", + optional => 1, + type => 'string', + format => $bwlimit_format, +}); + sub pve_parse_startup_order { my ($value) = @_; @@ -431,37 +497,6 @@ PVE::JSONSchema::register_standard_option('pve-startup-order', { typetext => '[[order=]\d+] [,up=\d+] [,down=\d+] ', }); -my $replicate_fmt = { - target => { - default_key => 1, - description => "Storage replication target node.", - type => 'string', format => 'pve-node', - format_description => "node", - }, - rate => { - description => "Rate limit in mbps (megabytes per second) as floating point number.", - type => 'number', - minimum => 1, - optional => 1, - }, - interval => { - description => "Storage replication sync interval in minutes. If set to zero replication is disabled.", - type => 'integer', - minimum => 0, - maximum => 1440, - default => 15, - optional => 1, - }, -}; - -PVE::JSONSchema::register_format('pve-replicate', $replicate_fmt); - -PVE::JSONSchema::register_standard_option('pve-replicate', { - description => "Storage replication settings.", - type => 'string', format => 'pve-replicate', - optional => 1, -}); - sub check_format { my ($format, $value, $path) = @_; @@ -539,6 +574,13 @@ sub format_size { return "${tb}T"; }; +sub parse_boolean { + my ($bool) = @_; + return 1 if $bool =~ m/^(1|on|yes|true)$/i; + return 0 if $bool =~ m/^(0|off|no|false)$/i; + return undef; +} + sub parse_property_string { my ($format, $data, $path, $additional_properties) = @_; @@ -577,8 +619,7 @@ sub parse_property_string { die "invalid key in comma-separated list property: $k\n" 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; + $v = parse_boolean($v) // $v; } $res->{$k} = $v; } elsif ($part !~ /=/) { @@ -1048,6 +1089,11 @@ my $default_schema_noref = { optional => 1, description => "This provides the title of the property", }, + renderer => { + type => "string", + optional => 1, + description => "This is used to provide rendering hints to format cli command output.", + }, requires => { type => [ "string", "object" ], optional => 1, @@ -1122,6 +1168,11 @@ my $default_schema_noref = { }, }, }, + print_width => { + type => "integer", + description => "For CLI context, this defines the maximal width to print before truncating", + optional => 1, + }, } }; @@ -1179,11 +1230,21 @@ my $method_schema = { description => "Method needs special privileges - only pvedaemon can execute it", optional => 1, }, + download => { + type => 'boolean', + description => "Method downloads the file content (filename is the return value of the method).", + optional => 1, + }, proxyto => { type => 'string', description => "A parameter name. If specified, all calls to this method are proxied to the host contained in that parameter.", optional => 1, }, + proxyto_callback => { + type => 'coderef', + description => "A function which is called to resolve the proxyto attribute. The default implementaion returns the value of the 'proxyto' parameter.", + optional => 1, + }, permissions => { type => 'object', description => "Required access permissions. By default only 'root' is allowed to access this method.", @@ -1306,7 +1367,7 @@ sub method_get_child_link { # a way to parse command line parameters, using a # schema to configure Getopt::Long sub get_options { - my ($schema, $args, $arg_param, $fixed_param, $pwcallback) = @_; + my ($schema, $args, $arg_param, $fixed_param, $param_mapping_hash) = @_; if (!$schema || !$schema->{properties}) { raise("too many arguments\n", code => HTTP_BAD_REQUEST) @@ -1322,17 +1383,19 @@ sub get_options { $list_param = $arg_param; } + my @interactive = (); my @getopt = (); foreach my $prop (keys %{$schema->{properties}}) { my $pd = $schema->{properties}->{$prop}; next if $list_param && $prop eq $list_param; next if defined($fixed_param->{$prop}); - if ($prop eq 'password' && $pwcallback) { - # we do not accept plain password on input line, instead - # we turn this into a boolean option and ask for password below - # using $pwcallback() (for security reasons). - push @getopt, "$prop"; + my $mapping = $param_mapping_hash->{$prop}; + if ($mapping && $mapping->{interactive}) { + # interactive parameters such as passwords: make the argument + # optional and call the mapping function afterwards. + push @getopt, "$prop:s"; + push @interactive, [$prop, $mapping->{func}]; } elsif ($pd->{type} eq 'boolean') { push @getopt, "$prop:s"; } else { @@ -1372,13 +1435,24 @@ sub get_options { raise("too many arguments\n", code => HTTP_BAD_REQUEST) if scalar(@$args) != 0; } + } else { + if (ref($arg_param)) { + foreach my $arg_name (@$arg_param) { + if ($arg_name eq 'extra-args') { + $opts->{'extra-args'} = []; + } else { + raise("not enough arguments\n", code => HTTP_BAD_REQUEST); + } + } + } } - if (my $pd = $schema->{properties}->{password}) { - if ($pd->{type} ne 'boolean' && $pwcallback) { - if ($opts->{password} || !$pd->{optional}) { - $opts->{password} = &$pwcallback(); - } + foreach my $entry (@interactive) { + my ($opt, $func) = @$entry; + my $pd = $schema->{properties}->{$opt}; + my $value = $opts->{$opt}; + if (defined($value) || !$pd->{optional}) { + $opts->{$opt} = $func->($value); } } @@ -1404,10 +1478,8 @@ sub get_options { if ($pd->{type} eq 'boolean') { if ($opts->{$p} eq '') { $opts->{$p} = 1; - } elsif ($opts->{$p} =~ m/^(1|true|yes|on)$/i) { - $opts->{$p} = 1; - } elsif ($opts->{$p} =~ m/^(0|false|no|off)$/i) { - $opts->{$p} = 0; + } elsif (defined(my $bool = parse_boolean($opts->{$p}))) { + $opts->{$p} = $bool; } else { raise("unable to parse boolean option\n", code => HTTP_BAD_REQUEST); } @@ -1460,8 +1532,7 @@ sub parse_config { if ($schema->{properties}->{$key} && $schema->{properties}->{$key}->{type} eq 'boolean') { - $value = 1 if $value =~ m/^(1|on|yes|true)$/i; - $value = 0 if $value =~ m/^(0|off|no|false)$/i; + $value = parse_boolean($value) // $value; } $cfg->{$key} = $value; } else {