[pve-devel] r5050 - pve-common/trunk
svn-commits at proxmox.com
svn-commits at proxmox.com
Wed Aug 25 13:57:23 CEST 2010
Author: dietmar
Date: 2010-08-25 11:57:23 +0000 (Wed, 25 Aug 2010)
New Revision: 5050
Added:
pve-common/trunk/CLIHandler.pm
Modified:
pve-common/trunk/ChangeLog
pve-common/trunk/Exception.pm
pve-common/trunk/JSONSchema.pm
pve-common/trunk/Makefile
pve-common/trunk/RESTHandler.pm
Log:
* Exception.pm (raise_param_exc): allow to specify usage information.
* RESTHandler.pm (usage_str): first try to autogenerate usage information.
(cli_handler2): experimental code used by new CLIHandler.pm
* CLIHandler.pm: new class for command line tools like 'pvesm' -
automatically create 'help' and usage information.
Added: pve-common/trunk/CLIHandler.pm
===================================================================
--- pve-common/trunk/CLIHandler.pm (rev 0)
+++ pve-common/trunk/CLIHandler.pm 2010-08-25 11:57:23 UTC (rev 5050)
@@ -0,0 +1,84 @@
+package PVE::CLIHandler;
+
+use strict;
+use warnings;
+
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $cmddef;
+
+__PACKAGE__->register_method ({
+ name => 'help',
+ path => 'help',
+ method => 'GET',
+ description => "Get help about specified command",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ cmd => {
+ description => "Command name",
+ type => 'string',
+ },
+ },
+ },
+ returns => { type => 'null' },
+
+ code => sub {
+ my ($param) = @_;
+
+ my $cmd = $param->{cmd};
+
+ my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}};
+
+ raise_param_exc({ cmd => "no such command '$cmd'"}) if !$class;
+
+ my $str = $class->usage_str($name, "pvesm $cmd", $arg_param, $uri_param, 'full');
+ print $str;
+
+ return undef;
+
+ }});
+
+sub print_usage_short {
+ my ($exename, $msg) = @_;
+
+ print STDERR "ERROR: $msg\n" if $msg;
+ print STDERR "USAGE: pvesm <COMMAND> [ARGS] [OPTIONS]\n";
+
+ foreach my $cmd (sort keys %$cmddef) {
+ my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}};
+ my $str = $class->usage_str($name, "pvesm $cmd", $arg_param, $uri_param, 'short');
+ print STDERR " $str";
+ }
+}
+
+sub handle_cmd {
+ my ($def, $exename, $cmd, $args) = @_;
+
+ $cmddef = $def;
+
+ $cmddef->{help} = [ __PACKAGE__, 'help', ['cmd'] ];
+
+ if (!$cmd) {
+ print_usage_short ($exename, "no command specified");
+ exit (-1);
+ }
+
+ my ($class, $name, $arg_param, $uri_param, $outsub) = @{$cmddef->{$cmd}} if $cmddef->{$cmd};
+
+ if (!$class) {
+ print_usage_short ($exename, "unknown command '$cmd'");
+ exit (-1);
+ }
+
+ my $prefix = "$exename $cmd";
+ my $res = $class->cli_handler2($prefix, $name, \@ARGV, $arg_param, $uri_param);
+ if ($outsub) {
+ &$outsub($res);
+ }
+}
+
+1;
Modified: pve-common/trunk/ChangeLog
===================================================================
--- pve-common/trunk/ChangeLog 2010-08-24 11:47:34 UTC (rev 5049)
+++ pve-common/trunk/ChangeLog 2010-08-25 11:57:23 UTC (rev 5050)
@@ -1,3 +1,13 @@
+2010-08-25 Proxmox Support Team <support at proxmox.com>
+
+ * Exception.pm (raise_param_exc): allow to specify usage information.
+
+ * RESTHandler.pm (usage_str): first try to autogenerate usage information.
+ (cli_handler2): experimental code used by new CLIHandler.pm
+
+ * CLIHandler.pm: new class for command line tools like 'pvesm' -
+ automatically create 'help' and usage information.
+
2010-08-24 Proxmox Support Team <support at proxmox.com>
* RESTHandler.pm (handle): remove $conn parameter. We use new
Modified: pve-common/trunk/Exception.pm
===================================================================
--- pve-common/trunk/Exception.pm 2010-08-24 11:47:34 UTC (rev 5049)
+++ pve-common/trunk/Exception.pm 2010-08-25 11:57:23 UTC (rev 5050)
@@ -48,12 +48,23 @@
die $exc;
}
+sub is_param_exc {
+ my ($self) = @_;
+
+ return $self->{code} && $self->{code} eq HTTP_BAD_REQUEST;
+}
+
sub raise_param_exc {
- my ($errors) = @_;
+ my ($errors, $usage) = @_;
- my $exc = PVE::Exception->new("Parameter verification failed.\n",
- code => HTTP_BAD_REQUEST,
- errors => $errors);
+ my $param = {
+ code => HTTP_BAD_REQUEST,
+ errors => $errors,
+ };
+
+ $param->{usage} = $usage if $usage;
+
+ my $exc = PVE::Exception->new("Parameter verification failed.\n", %$param);
my ($pkg, $filename, $line) = caller;
@@ -89,6 +100,11 @@
}
}
+ if ($self->{usage}) {
+ $msg .= $self->{usage};
+ $msg .= "\n" if $msg !~ m/\n$/;
+ }
+
return $msg;
}
Modified: pve-common/trunk/JSONSchema.pm
===================================================================
--- pve-common/trunk/JSONSchema.pm 2010-08-24 11:47:34 UTC (rev 5049)
+++ pve-common/trunk/JSONSchema.pm 2010-08-25 11:57:23 UTC (rev 5050)
@@ -650,7 +650,7 @@
my ($schema, $args, $uri_param, $pwcallback) = @_;
if (!$schema || !$schema->{properties}) {
- die "too many arguments\n"
+ raise("too many arguments\n", code => HTTP_BAD_REQUEST)
if scalar(@$args) != 0;
return {};
}
@@ -671,10 +671,10 @@
}
my $opts = {};
- die "unable to parse option\n"
+ raise("unable to parse option\n", code => HTTP_BAD_REQUEST)
if !Getopt::Long::GetOptionsFromArray($args, $opts, @getopt);
- die "too many arguments\n"
+ raise("too many arguments\n", code => HTTP_BAD_REQUEST)
if scalar(@$args) != 0;
if (my $pd = $schema->{properties}->{password}) {
Modified: pve-common/trunk/Makefile
===================================================================
--- pve-common/trunk/Makefile 2010-08-24 11:47:34 UTC (rev 5049)
+++ pve-common/trunk/Makefile 2010-08-25 11:57:23 UTC (rev 5050)
@@ -19,6 +19,7 @@
LIB_SOURCES= \
RPCEnvironment.pm \
+ CLIHandler.pm \
RESTHandler.pm \
JSONSchema.pm \
SafeSyslog.pm \
Modified: pve-common/trunk/RESTHandler.pm
===================================================================
--- pve-common/trunk/RESTHandler.pm 2010-08-24 11:47:34 UTC (rev 5049)
+++ pve-common/trunk/RESTHandler.pm 2010-08-25 11:57:23 UTC (rev 5050)
@@ -3,7 +3,7 @@
use strict;
use warnings;
use PVE::SafeSyslog;
-use PVE::Exception;
+use PVE::Exception qw(raise raise_param_exc);
use PVE::JSONSchema;
use HTTP::Status qw(:constants :is status_message);
@@ -194,6 +194,148 @@
return $result;
}
+sub usage_str {
+ my ($self, $name, $prefix, $arg_param, $fixed_param, $format) = @_;
+
+ # format: 'long', 'short', 'full'
+ #' long' ... default
+ # 'short' ... command line only (one line)
+ # 'full' ... include description
+
+ $format = 'long' if !$format;
+
+ my $info = $self->map_method_by_name($name);
+
+ my $out = '';
+
+ my $arg_hash = {};
+
+ my $args = '';
+ foreach my $p (@$arg_param) {
+ $arg_hash->{$p} = 1;
+ $args .= " " if $args;
+ $args .= "<$p>";
+ }
+
+ my $get_type_str = sub {
+ my ($phash) = @_;
+
+ my $type = $phash->{format} || $phash->{type} || 'string';
+
+ return $type;
+ };
+
+ my $get_prop_descr = sub {
+ my ($phash, $display_name) = @_;
+
+ my $res = '';
+
+ my $descr = $phash->{description} || "no description available";
+ my $type = &$get_type_str($phash);
+
+ $res .= sprintf " %-10s %-10s $descr\n", $display_name, "$type";
+ my $indend = " ";
+ if ($phash->{enum}) {
+ $res .= sprintf "${indend}possible values: %s\n", join ('|', @{$phash->{enum}});
+ }
+ if ($phash->{minimum}) {
+ $res .= "${indend}minimum: $phash->{minimum}\n";
+ }
+ if ($phash->{maximum}) {
+ $res .= "${indend}maximum: $phash->{maximum}\n";
+ }
+
+ return $res;
+ };
+
+ my $schema = $info->{parameters};
+ my $prop = $schema->{properties};
+
+ my $argdescr = '';
+ foreach my $k (@$arg_param) {
+ next if defined($fixed_param->{$k}); # just to be sure
+
+ $argdescr .= &$get_prop_descr($prop->{$k}, "<$k>");
+ }
+
+ my $opts = '';
+ foreach my $k (keys %$prop) {
+ next if $arg_hash->{$k};
+ next if defined($fixed_param->{$k});
+
+ my $type = &$get_type_str($prop->{$k});
+
+ $opts .= &$get_prop_descr($prop->{$k}, "-$k");
+
+ if (!$prop->{$k}->{optional}) {
+ $args .= " " if $args;
+ $args .= "-$k $type"
+ }
+ }
+
+ $out .= "usage: " if $format ne 'short';
+
+ $out .= "$prefix $args";
+
+ $out .= $opts ? " [OPTIONS]\n" : "\n";
+
+ return $out if $format eq 'short';
+
+ $out .= $argdescr if $argdescr;
+
+ $out .= $opts if $opts;
+
+ if ($info->{description} && $format eq 'full') {
+ $out .= "\nDescription: $info->{description}\n";
+ }
+ return $out;
+}
+
+sub cli_handler2 {
+ my ($self, $prefix, $name, $args, $arg_param, $fixed_param, $pwcallback) = @_;
+
+ my $info = $self->map_method_by_name($name);
+
+ my $param;
+ foreach my $p (keys %$fixed_param) {
+ $param->{$p} = $fixed_param->{$p};
+ }
+
+ foreach my $p (@$arg_param) {
+ my $v = shift @$args;
+
+ if (!defined($v)) {
+ my $usage = $self->usage_str($name, $prefix, $arg_param, $fixed_param);
+ raise_param_exc({ $p => "got undefined value for required argument"}, $usage);
+ }
+
+ if ($v =~ m/^-/) {
+ my $usage = $self->usage_str($name, $prefix, $arg_param, $fixed_param);
+ raise_param_exc({ $p => "required argument may not start with hyphen"}, $usage);
+ }
+
+ $param->{$p} = $v;
+ }
+
+ my $res;
+ eval {
+ my $param = PVE::JSONSchema::get_options($info->{parameters}, $args, $param, $pwcallback);
+
+ $res = $self->handle($info, $param);
+ };
+ if (my $err = $@) {
+ my $ec = ref($err);
+
+ die $err if !$ec || $ec ne "PVE::Exception" || !$err->is_param_exc();
+
+ $err->{usage} = $self->usage_str($name, $prefix, $arg_param, $fixed_param);
+
+ die $err;
+ }
+
+ return $res;
+}
+
sub cli_handler {
my ($self, $name, $args, $uri_param, $pwcallback) = @_;
More information about the pve-devel
mailing list