JSONSchema: register new standard option 'pve-output-format'
[pve-common.git] / src / PVE / JSONSchema.pm
index 02aa2ab..aa82167 100644 (file)
@@ -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 {