cli: more generic interactive parameter definition
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Mon, 12 Mar 2018 12:04:15 +0000 (13:04 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 13 Mar 2018 10:04:58 +0000 (11:04 +0100)
Instead of hardcoding 'password' as a special case in the
JSONSchema's getopt handling, extend the new parameter
mapping to allow defining a parameters as 'interactive'.
They also take an optional argument on the command line
directly.

This effectively deprecates the password special case which
should be replaced in pct/pveum/... and then dropped in
pve-common.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
src/PVE/JSONSchema.pm
src/PVE/RESTHandler.pm

index 9861a8f..0e722b8 100644 (file)
@@ -1333,7 +1333,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, $pwcallback, $param_mapping_hash) = @_;
 
     if (!$schema || !$schema->{properties}) {
        raise("too many arguments\n", code => HTTP_BAD_REQUEST)
@@ -1349,13 +1349,20 @@ 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) {
+       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 ($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).
@@ -1409,6 +1416,15 @@ sub get_options {
        }
     }
 
+    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);
+       }
+    }
+
     # decode after Getopt as we are not sure how well it handles unicode
     foreach my $p (keys %$opts) {
        if (!ref($opts->{$p})) {
index 0a64158..3f5c732 100644 (file)
@@ -514,15 +514,15 @@ my $compute_param_mapping_hash = sub {
     return $res if !defined($mapping_array);
 
     foreach my $item (@$mapping_array) {
-       my ($name, $func, $desc);
+       my ($name, $func, $desc, $interactive);
        if (ref($item) eq 'ARRAY') {
-           ($name, $func, $desc) = @$item;
+           ($name, $func, $desc, $interactive) = @$item;
        } else {
            $name = $item;
            $func = sub { return PVE::Tools::file_get_contents($_[0]) };
        }
        $desc //= '<filepath>';
-       $res->{$name} = { desc => $desc, func => $func };
+       $res->{$name} = { desc => $desc, func => $func, interactive => $interactive };
     }
 
     return $res;
@@ -691,6 +691,7 @@ my $replace_file_names_with_contents = sub {
     my ($param, $param_mapping_hash) = @_;
 
     while (my ($k, $d) = each %$param_mapping_hash) {
+       next if $d->{interactive}; # handled by the JSONSchema's get_options code
        $param->{$k} = $d->{func}->($param->{$k})
            if defined($param->{$k});
     }
@@ -705,10 +706,10 @@ sub cli_handler {
 
     my $res;
     eval {
-       my $param = PVE::JSONSchema::get_options($info->{parameters}, $args, $arg_param, $fixed_param, $read_password_func);
+       my $param_mapping_hash = $compute_param_mapping_hash->($param_mapping_func->($name)) if $param_mapping_func;
+       my $param = PVE::JSONSchema::get_options($info->{parameters}, $args, $arg_param, $fixed_param, $read_password_func, $param_mapping_hash);
 
-       if (defined($param_mapping_func)) {
-           my $param_mapping_hash = $compute_param_mapping_hash->(&$param_mapping_func($name));
+       if (defined($param_mapping_hash)) {
            &$replace_file_names_with_contents($param, $param_mapping_hash);
        }