[pve-devel] r4966 - pve-common/trunk
svn-commits at proxmox.com
svn-commits at proxmox.com
Wed Aug 11 11:46:09 CEST 2010
Author: dietmar
Date: 2010-08-11 09:46:09 +0000 (Wed, 11 Aug 2010)
New Revision: 4966
Added:
pve-common/trunk/RESTHandler.pm
Modified:
pve-common/trunk/Makefile
Log:
moved RESTHandler from pve-manager
Modified: pve-common/trunk/Makefile
===================================================================
--- pve-common/trunk/Makefile 2010-08-10 13:00:32 UTC (rev 4965)
+++ pve-common/trunk/Makefile 2010-08-11 09:46:09 UTC (rev 4966)
@@ -18,6 +18,7 @@
DEB=${PACKAGE}_${VERSION}-${PKGREL}_${ARCH}.deb
LIB_SOURCES= \
+ RESTHandler.pm \
JSONSchema.pm \
SafeSyslog.pm \
AtomicFile.pm \
Added: pve-common/trunk/RESTHandler.pm
===================================================================
--- pve-common/trunk/RESTHandler.pm (rev 0)
+++ pve-common/trunk/RESTHandler.pm 2010-08-11 09:46:09 UTC (rev 4966)
@@ -0,0 +1,243 @@
+package PVE::RESTHandler;
+
+use strict;
+use warnings;
+use PVE::SafeSyslog;
+use PVE::JSONSchema;
+use HTTP::Status qw(:constants :is status_message);
+
+use Data::Dumper; # fixme: remove
+
+my $method_registry = {};
+my $method_by_name = {};
+
+our $AUTOLOAD; # it's a package global
+
+sub register_method {
+ my ($self, $info) = @_;
+
+ PVE::JSONSchema::validate_method_info($info);
+
+ my $match_re = [];
+ my $match_name = [];
+
+ foreach my $comp (split(/\/+/, $info->{path})) {
+ die "path compoment has zero length" if $comp eq '';
+ if ($comp =~ m/^\{(\w+)\}$/) {
+ my $name = $1;
+ push @$match_re, '\S+';
+ push @$match_name, $1;
+ } else {
+ push @$match_re, $comp;
+ push @$match_name, undef;
+ }
+ }
+
+ $info->{match_re} = $match_re;
+ $info->{match_name} = $match_name;
+
+ $method_by_name->{$self} = {} if !defined($method_by_name->{$self});
+
+ if ($info->{name}) {
+ die "method '${self}::$info->{name}' already defined\n"
+ if defined($method_by_name->{$self}->{$info->{name}});
+
+ $method_by_name->{$self}->{$info->{name}} = $info;
+ }
+
+ push @{$method_registry->{$self}}, $info;
+}
+
+sub AUTOLOAD {
+ my $self = shift;
+
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+
+ my $info = $method_by_name->{$self}->{$method};
+
+ die "no such method '${self}::$method'\n" if !$info;
+
+ # fixme: how do we handle this here?
+ # fixme: language ?
+ my $conn = {
+# abs_uri => $abs_uri,
+# rel_uri => $rel_uri,
+# user => $username,
+ params => shift || {},
+ };
+
+ my $res = {};
+ $res->{status} = $self->handle($info, $conn, $res);
+
+ my $status = $res->{status};
+ if (!is_success($status)) {
+ my $msg = $res->{message} || status_message($status);
+ chomp $msg;
+ $msg .= "\n";
+ if ($res->{errors}) {
+ foreach my $e (keys %{$res->{errors}}) {
+ $msg .= "$e: $res->{errors}->{$e}\n";
+ }
+ }
+ die $msg;
+ }
+
+ return $res->{data};
+}
+
+sub method_attributes {
+ my ($self) = @_;
+
+ return $method_registry->{$self};
+}
+
+sub map_method {
+ my ($self, $stack, $method, $uri_param) = @_;
+
+ my $ma = $method_registry->{$self};
+
+ my $stacklen = scalar(@$stack);
+
+ #syslog ('info', "MAPTEST:$method:$self: " . join ('/', @$stack));
+
+ foreach my $info (@$ma) {
+ #syslog ('info', "TEST0 " . Dumper($info));
+ next if !($info->{subclass} || ($info->{method} eq $method));
+ my $regexlen = scalar(@{$info->{match_re}});
+ if ($info->{subclass}) {
+ next if $stacklen < $regexlen;
+ } else {
+ next if $stacklen != $regexlen;
+ }
+
+ #syslog ('info', "TEST1 " . Dumper($info));
+
+ my $param = {};
+ my $i = 0;
+ for (; $i < $regexlen; $i++) {
+ my $comp = $stack->[$i];
+ my $re = $info->{match_re}->[$i];
+ #print "COMPARE $comp $info->{match_re}->[$i]\n";
+ my ($match) = $stack->[$i] =~ m/^($re)$/;
+ last if !defined($match);
+ if (my $name = $info->{match_name}->[$i]) {
+ $param->{$name} = $match;
+ }
+ }
+
+ next if $i != $regexlen;
+
+ #print "MATCH $info->{name}\n";
+
+ foreach my $p (keys %$param) {
+ $uri_param->{$p} = $param->{$p};
+ }
+
+ return $info;
+ }
+}
+
+sub find_handler {
+ my ($class, $method, $stack, $uri_param) = @_;
+
+ my $info;
+ eval {
+ $info = $class->map_method($stack, $method, $uri_param);
+ };
+ syslog('err', $@) if $@;
+
+ return undef if !$info;
+
+ if (my $subh = $info->{subclass}) {
+ eval "require $subh;";
+
+ if ($@) {
+ syslog ('err', "missing subclass '$subh': $@");
+ return undef;
+ }
+
+ my $matchlen = scalar(@{$info->{match_re}});
+
+ for (my $i = 0; $i < $matchlen; $i++) {
+ my $fragment = shift @$stack;
+ # fixme: store $fragments somewhere ?
+ }
+
+ return $subh->find_handler($method, $stack, $uri_param);
+ }
+
+ return ($class, $info);
+}
+
+sub handle {
+ my ($self, $info, $conn, $resp) = @_;
+
+ my $func = $info->{code};
+
+ if (!($info->{name} && $func)) {
+ $resp->{message} = "Method lookup failed ('$info->{name}')";
+ $resp->{status} = HTTP_INTERNAL_SERVER_ERROR;
+ return $resp->{status};
+ }
+
+ if (my $schema = $info->{parameters}) {
+ # warn "validate ". Dumper($conn->{params}) . "\n" . Dumper($schema);
+ my $res = PVE::JSONSchema::validate($conn->{params}, $schema);
+ if (!$res->{valid}) {
+ $resp->{status} = HTTP_BAD_REQUEST;
+ $resp->{message} = "Parameter verification failed";
+ $resp->{errors} = $res->{errors},
+ return $resp->{status};
+ }
+ }
+
+ eval{
+ my $result = &$func($conn, $resp, $conn->{params});
+ $resp->{status} = HTTP_OK if !$resp->{status};
+ $resp->{data} = $result;
+ };
+ my $err = $@;
+
+ if ($err) {
+ $resp->{message} = $err;
+
+ $resp->{status} = HTTP_BAD_REQUEST
+ if !($resp->{status} && is_error($resp->{status}));
+ }
+
+ # fixme: this is only to be safe
+ if (!$err && (my $schema = $info->{returns})) {
+
+ my $res = PVE::JSONSchema::validate($resp->{data}, $schema);
+ if (!$res->{valid}) {
+
+ $resp->{message} = "Result verification vailed";
+ $resp->{status} = HTTP_INTERNAL_SERVER_ERROR;
+ $resp->{errors} = $res->{errors};
+
+ return $resp->{status};
+ }
+ }
+
+ return $resp->{status};
+}
+
+# utility methods
+# note: this modifies the original hash by adding the id property
+sub hash_to_array {
+ my ($hash, $idprop) = @_;
+
+ my $res = [];
+ return $res if !$hash;
+
+ foreach my $k (keys %$hash) {
+ $hash->{$k}->{$idprop} = $k;
+ push @$res, $hash->{$k};
+ }
+
+ return $res;
+}
+
+1;
More information about the pve-devel
mailing list