[pve-devel] r4830 - in pve-manager/pve2: bin bin/init.d lib/PVE
svn-commits at proxmox.com
svn-commits at proxmox.com
Tue Jun 22 13:37:05 CEST 2010
Author: dietmar
Date: 2010-06-22 11:37:05 +0000 (Tue, 22 Jun 2010)
New Revision: 4830
Added:
pve-manager/pve2/lib/PVE/API2Client.pm
pve-manager/pve2/lib/PVE/APIDaemon.pm
Modified:
pve-manager/pve2/bin/init.d/pvedaemon
pve-manager/pve2/bin/pvedaemon
pve-manager/pve2/lib/PVE/Makefile.am
Log:
started pvedaemon rewrite
Modified: pve-manager/pve2/bin/init.d/pvedaemon
===================================================================
--- pve-manager/pve2/bin/init.d/pvedaemon 2010-06-22 06:13:04 UTC (rev 4829)
+++ pve-manager/pve2/bin/init.d/pvedaemon 2010-06-22 11:37:05 UTC (rev 4830)
@@ -2,8 +2,8 @@
### BEGIN INIT INFO
# Provides: pvedaemon
-# Required-Start: $network $syslog
-# Required-Stop: $network $syslog
+# Required-Start: $remote_fs $network $syslog
+# Required-Stop: $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start PVE Daemon
@@ -23,7 +23,7 @@
case "$1" in
start)
log_daemon_msg "Starting PVE daemon" "pvedaemon"
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -p $PIDFILE
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON
log_end_msg $?
;;
stop)
@@ -36,7 +36,7 @@
if ( [ -e $PIDFILE ] && kill -0 `cat $PIDFILE`) then
start-stop-daemon --stop --signal HUP --pidfile $PIDFILE
else
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -p $PIDFILE
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON
fi
log_end_msg $?
;;
@@ -44,7 +44,7 @@
log_daemon_msg "Restarting PVE daemon" "pvedaemon"
start-stop-daemon --stop --quiet --retry TERM/2/TERM/10/KILL --pidfile $PIDFILE
sleep 2
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -p $PIDFILE
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON
log_end_msg $?
;;
*)
Modified: pve-manager/pve2/bin/pvedaemon
===================================================================
--- pve-manager/pve2/bin/pvedaemon 2010-06-22 06:13:04 UTC (rev 4829)
+++ pve-manager/pve2/bin/pvedaemon 2010-06-22 11:37:05 UTC (rev 4830)
@@ -1,28 +1,25 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl -T -w
+$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
+
+delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
+
use strict;
use Getopt::Long;
use POSIX ":sys_wait_h";
-use SOAP::Transport::HTTP;
-use PVE::Config;
-use PVE::ConfigServer;
+use Socket;
+use PVE::Utils;
use PVE::SafeSyslog;
+# use PVE::Config; # fixme
+use PVE::APIDaemon;
-my $opt_pidfile;
+my $pidfile = "/var/run/pvedaemon.pid";
my $opt_debug;
-# restrict access exported methods
-my $filterdata = PVE::ConfigServer::filter_data();
-
-my @methods = keys %{$filterdata->{soap_exports}};
-
initlog ('pvedaemon', 'daemon');
-$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
-
-if (!GetOptions ('pidfile=s' => \$opt_pidfile,
- 'debug' => \$opt_debug)) {
- die "usage: $0 [pidfile=s]\n";
+if (!GetOptions ('debug' => \$opt_debug)) {
+ die "usage: $0 [--debug]\n";
}
$SIG{'__WARN__'} = sub {
@@ -36,15 +33,14 @@
$0 = "pvedaemon";
my $cpid;
-
-my ($soaphost, $soapport) = PVE::Config::soap_host_port();
-
+my $daemon;
eval {
- $pve_config_daemon = PVE::SOAPTransport
- -> new (LocalAddr => $soaphost, LocalPort => $soapport,
- ReuseAddr => 1, Proto => 'tcp',
- serializer => PVE::SOAPSerializer->new)
- -> dispatch_to(map {"PVE::ConfigServer::$_"} @methods);
+ $daemon = PVE::APIDaemon->new(
+ LocalAddr => "127.0.0.1",
+ LocalPort => 85,
+ Listen => SOMAXCONN,
+ ReuseAddr => 1,
+ );
};
my $err = $@;
@@ -65,7 +61,7 @@
$SIG{INT} = 'DEFAULT';
- unlink "$opt_pidfile" if ($opt_pidfile);
+ unlink "$pidfile";
exit (0);
};
@@ -86,7 +82,7 @@
system ("echo > /var/lib/pve-manager/vmops"); # init vmops file
eval {
- $pve_config_daemon->handle;
+ $daemon->start_server();
};
my $err = $@;
@@ -98,13 +94,11 @@
} else {
- if ($opt_pidfile) {
- open (PIDFILE, ">$opt_pidfile") ||
- die "cant write '$opt_pidfile' - $! :ERROR";
- print PIDFILE "$cpid\n";
- close (PIDFILE) ||
- die "cant write '$opt_pidfile' - $! :ERROR";
- }
+ open (PIDFILE, ">$pidfile") ||
+ die "cant write '$pidfile' - $! :ERROR";
+ print PIDFILE "$cpid\n";
+ close (PIDFILE) ||
+ die "cant write '$pidfile' - $! :ERROR";
}
exit (0);
@@ -117,28 +111,12 @@
=head1 SYNOPSIS
-pvedaemon [--pid <PIDFILE>] [--debug]
+pvedaemon [--debug]
=head1 DESCRIPTION
-All configuration is done using this SOAP Server. The Server only
-listens to a local address 127.0.0.1 port 83 for security
-reasons. Simple tunnel this address if you want to do remote
-administration - for example:
+All configuration is done using this Server. The Server only
+listens to a local address 127.0.0.1 port 85 for security
+reasons.
- ssh -N -L 10000:localhost:83 <SERVER_IP>
-The server can do HTTP basic authentication, and uses ticket
-authentication if your SOAP client supports cookies. An example
-can be found in:
-
- /usr/share/doc/pve-manager/examples/testauth.pl
-
-NOTE: Do not open access to this server for untrusted
-users (local system users are considered trusted - at least we
-assume they normally do not start DOS attacks).
-
-
-
-
-
Added: pve-manager/pve2/lib/PVE/API2Client.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2Client.pm (rev 0)
+++ pve-manager/pve2/lib/PVE/API2Client.pm 2010-06-22 11:37:05 UTC (rev 4830)
@@ -0,0 +1,98 @@
+package PVE::API2Client;
+
+use strict;
+use warnings;
+use URI;
+use HTTP::Cookies;
+use LWP::UserAgent;
+use JSON;
+use PVE::API2;
+
+sub BEGIN {
+ no strict 'refs';
+ #print "making accessors\n";
+
+ foreach my $member (qw (test)) {
+
+ *{$member} = sub {
+ my ($self, %param) = @_;
+
+ #print "wrapper called\n";
+
+ my $func = PVE::API2->can($member) ||
+ die "no such method";
+
+ my $method = 'POST';
+
+ $method = "GET" if PVE::API2->check_attribute($member, "GET");
+
+ my $uri = URI->new("http://$self->{config}->{host}/api2/$member");
+
+ if ($method eq 'GET') {
+ $uri->query_form(\%param);
+ }
+
+ #print "CALL : " . $uri->as_string() . "\n";
+ #print "PATH : " . $uri->path() . "\n";
+
+ my $ua = $self->{useragent};
+
+ my $response;
+ if ($method eq 'GET') {
+ $response = $ua->get($uri);
+ } elsif ($method eq 'POST') {
+ $response = $ua->post($uri, \%param);
+ } else {
+ die "method $method not implemented\n";
+ }
+
+ if ($response->is_success) {
+ my $ct = $response->header('Content-Type');
+
+ die "got unexpected content type" if $ct ne 'application/json';
+
+ my $data = decode_json($response->decoded_content);
+
+ die $data->{error} if $data->{error};
+
+ return $data;
+
+ } else {
+ die $response->status_line . "\n";
+ }
+
+ };
+ }
+}
+
+sub new {
+ my ($class, %param) = @_;
+
+ my $self = {
+ config => {
+ ticket => $param{ticket},
+ username => $param{username},
+ password => $param{password},
+ host => $param{host} || 'localhost',
+ port => $param{port} || 85,
+ timeout => $param{timeout} || 60,
+ },
+ };
+ bless $self;
+
+ $self->{cookie_jar} = HTTP::Cookies->new (ignore_discard => 1);
+
+ if ($self->{config}->{ticket}) {
+ $self->{cookie_jar}->set_cookie(0, 'PVEAuthTicket', $self->{config}->{ticket},
+ '/', $self->{config}->{host});
+ }
+
+ $self->{useragent} = LWP::UserAgent->new(
+ timeout => $self->{config}->{timeout},
+ cookie_jar => $self->{cookie_jar},
+ );
+
+ return $self;
+}
+
+1;
Property changes on: pve-manager/pve2/lib/PVE/API2Client.pm
___________________________________________________________________
Added: svn:executable
+ *
Added: pve-manager/pve2/lib/PVE/APIDaemon.pm
===================================================================
--- pve-manager/pve2/lib/PVE/APIDaemon.pm (rev 0)
+++ pve-manager/pve2/lib/PVE/APIDaemon.pm 2010-06-22 11:37:05 UTC (rev 4830)
@@ -0,0 +1,335 @@
+package PVE::APIDaemon;
+
+use strict;
+use warnings;
+use vars qw(@ISA);
+use MIME::Base64;
+#use Sys::Syslog;
+use PVE::SafeSyslog;
+use PVE::Config;
+use POSIX qw(EINTR);
+use POSIX ":sys_wait_h";
+use IO::Handle;
+use IO::Select;
+use vars qw(@ISA);
+use HTTP::Daemon;
+use HTTP::Status qw(:constants);
+use HTTP::Request::Params; # fixme: remove
+use Data::Dumper; # fixme: remove
+use URI;
+use JSON;
+use PVE::API2;
+
+# This is a quite simple pre-fork server
+
+ at ISA = qw(HTTP::Daemon);
+
+my $documentroot = "/usr/share/pve-api/root";
+
+my $workers = {};
+
+my $max_workers = 3; # pre-forked worker processes
+my $max_requests = 500; # max requests per worker
+my $child_terminate = 0;
+my $child_reload_config = 0;
+
+sub worker_finished {
+ my $cpid = shift;
+
+ syslog ('info', "worker $cpid finished");
+}
+
+sub finish_workers {
+ local $!; local $?;
+ foreach my $cpid (keys %$workers) {
+ my $waitpid = waitpid ($cpid, WNOHANG);
+ if (defined($waitpid) && ($waitpid == $cpid)) {
+ delete ($workers->{$cpid});
+ worker_finished ($cpid);
+ }
+ }
+}
+
+sub test_workers {
+ foreach my $cpid (keys %$workers) {
+ if (!kill(0, $cpid)) {
+ waitpid($cpid, POSIX::WNOHANG());
+ delete $workers->{$cpid};
+ worker_finished ($cpid);
+ }
+ }
+}
+
+sub start_workers {
+ my $self = shift;
+
+ my $count = 0;
+ foreach my $cpid (keys %$workers) {
+ $count++;
+ }
+
+ my $need = $max_workers - $count;
+
+ return if $need <= 0;
+
+ syslog ('info', "starting $need worker(s)");
+
+ while ($need > 0) {
+ my $pid = fork;
+
+ if (!defined ($pid)) {
+ syslog ('err', "can't fork worker");
+ sleep (1);
+ } elsif ($pid) { #parent
+ $workers->{$pid} = 1;
+ $0 = 'pvedaemon worker';
+ syslog ('info', "worker $pid started");
+ $need--;
+ } else {
+ $SIG{TERM} = $SIG{QUIT} = sub {
+ $child_terminate = 1;
+ };
+
+ $SIG{USR1} = sub {
+ $child_reload_config = 1;
+ };
+
+ eval {
+ # try to init inotify
+ PVE::Config::inotify_init();
+
+ $self->handle_requests ();
+ };
+ syslog ('err', $@) if $@;
+
+ exit (0);
+ }
+ }
+}
+
+sub terminate_server {
+
+ syslog('info', "received terminate request");
+
+ foreach my $cpid (keys %$workers) {
+ kill (15, $cpid); # TERM childs
+ }
+
+ # nicely shutdown childs (give them max 10 seconds to shut down)
+ my $previous_alarm = alarm (10);
+ eval {
+ local $SIG{ALRM} = sub { die "Timed Out!\n" };
+
+ while ((my $pid = waitpid (-1, 0)) > 0) {
+ if (defined($workers->{$pid})) {
+ delete ($workers->{$pid});
+ worker_finished ($pid);
+ }
+ }
+
+ };
+ alarm ($previous_alarm);
+
+ foreach my $cpid (keys %$workers) {
+ # KILL childs still alive!
+ if (kill (0, $cpid)) {
+ delete ($workers->{$cpid});
+ syslog("err", "kill worker $cpid");
+ kill (9, $cpid);
+ }
+ }
+
+}
+
+sub new {
+ my $class = shift;
+
+ my $self = $class->SUPER::new(@_) ||
+ die "unable to create socket - $@\n";
+
+ return $self;
+}
+
+sub start_server {
+ my $self = shift;
+
+ eval {
+ my $old_sig_chld = $SIG{CHLD};
+ local $SIG{CHLD} = sub {
+ finish_workers ();
+ &$old_sig_chld(@_) if $old_sig_chld;
+ };
+
+ my $old_sig_term = $SIG{TERM};
+ local $SIG{TERM} = sub {
+ terminate_server ();
+ &$old_sig_term(@_) if $old_sig_term;
+ };
+ local $SIG{QUIT} = sub {
+ terminate_server();
+ &$old_sig_term(@_) if $old_sig_term;
+ };
+
+ local $SIG{USR1} = 'IGNORE';
+
+ local $SIG{HUP} = sub {
+ syslog ("info", "received reload request");
+ foreach my $cpid (keys %$workers) {
+ kill (10, $cpid); # SIGUSR1 childs
+ }
+ };
+
+ for (;;) { # forever
+ $self->start_workers ();
+ sleep (5);
+ $self->test_workers ();
+ }
+ };
+ my $err = $@;
+
+ if ($err) {
+ syslog ('err', "ERROR: $err");
+ }
+}
+
+sub send_basic_auth_request {
+ my ($c) = @_;
+
+ my $realm = 'PVE API Daemon';
+ my $auth_request_res = HTTP::Response->new(401, 'Unauthorized');
+ $auth_request_res->header('WWW-Authenticate' => qq{Basic realm="$realm"});
+ $auth_request_res->is_error(1);
+ $auth_request_res->error_as_HTML(1);
+ $c->send_response($auth_request_res);
+}
+
+sub send_error {
+ my ($c, $code, $msg) = @_;
+
+ $c->send_response(HTTP::Response->new($code, $msg));
+}
+
+sub handle_login {
+ my ($daemon, $c, $r) = @_;
+
+ # my $cuser = ident_user ($c->peerport, $c->sockport);
+
+ my $h = $r->headers;
+
+ print "Head: " . Dumper($h) . "\n";
+}
+
+sub handle_requests {
+ my $self = shift;
+
+ my $rcount = 0;
+
+ my $sel = IO::Select->new();
+ $sel->add ($self);
+
+ my $timeout = 5;
+ my @ready;
+ while (1) {
+ if (scalar (@ready = $sel->can_read($timeout))) {
+
+ my $c;
+ while (($c = $self->accept) || ($! == EINTR)) {
+ next if !$c; # EINTR
+
+ if ($child_reload_config) {
+ $child_reload_config = 0;
+ syslog('info', "child reload config");
+ # fixme: anything to do here?
+ }
+
+ $c->timeout(5);
+
+ # fixme: limit max request length somehow
+
+ # handle requests
+ while (my $r = $c->get_request) {
+ print "Request: " . $r->as_string() . "\nEND REQUEST\n";
+ #print "Method: " . $r->method() . "\n";
+ #print "CALL: " . $r->uri->as_string() . "\n";
+ #print "PATH: " . $r->uri->path() . "\n";
+ #print "Content: " . $r->content() . "\n";
+
+ my $method = $r->method();
+
+ syslog('info', "$method: " . $r->uri->as_string());
+
+ my $headers = $r->headers;
+
+ my $ct = $headers->header('Content-Type');
+
+ my $parser = HTTP::Request::Params->new({req => $r});
+ my $params = $parser->params;
+
+ #print "params: " . Dumper($params) . "\n";
+
+ if ($method eq 'POST' || $method eq 'GET') {
+
+ my $path = $r->uri->path();
+ print "PATH: $path\n";
+
+ if ($path =~ m|^/api2/(\w+)$|) {
+
+ my $rpcmethod = $1;
+
+ my $data;
+ eval {
+
+ my $serv = bless {}, 'PVE::API2';
+
+ die "no such method\n" if !$serv->check_attribute($rpcmethod, "Public");
+
+ my $func = PVE::API2->can($rpcmethod) || die "no such method\n";
+
+ $data = &$func($serv, $params);
+
+ };
+ my $error = $@;
+
+ my $json = encode_json({ result => $data, error => $error || undef});
+
+ my $response = HTTP::Response->new(200);
+ my $x = length ($json);
+ $response->header("Content-Type" => 'application/json');
+ $response->header("Content-Length" => $x);
+ $response->header("Pragma", "no-cache");
+ $response->content($json);
+
+ #print "RESPONSE: " . $response->as_string . "\n";
+
+ $c->send_response($response);
+
+ } else {
+ $c->send_error(HTTP_FORBIDDEN);
+ }
+
+ } else {
+ $c->send_error(HTTP_METHOD_NOT_ALLOWED);
+ }
+ }
+ $rcount++;
+
+ # we only handle one request per connection, because
+ # we want to minimize the number of connections
+
+ $c->shutdown(2);
+ $c->close();
+ last;
+ }
+
+ last if $child_terminate || !$c || ($rcount >= $max_requests);
+
+ } else {
+ last if $child_terminate;
+
+ # timeout
+ PVE::Config::poll(); # read inotify events
+ }
+ }
+}
+
+1;
Property changes on: pve-manager/pve2/lib/PVE/APIDaemon.pm
___________________________________________________________________
Added: svn:executable
+ *
Modified: pve-manager/pve2/lib/PVE/Makefile.am
===================================================================
--- pve-manager/pve2/lib/PVE/Makefile.am 2010-06-22 06:13:04 UTC (rev 4829)
+++ pve-manager/pve2/lib/PVE/Makefile.am 2010-06-22 11:37:05 UTC (rev 4830)
@@ -5,6 +5,8 @@
PERLSOURCE = \
JSONSchema.pm \
API2.pm \
+ API2Client.pm \
+ APIDaemon.pm \
REST.pm \
RESTHandler.pm \
SourceFilter.pm \
More information about the pve-devel
mailing list