[LON-CAPA-cvs] cvs: modules /raeburn monitor.pl
raeburn
lon-capa-cvs@mail.lon-capa.org
Tue, 30 Jan 2007 15:51:51 -0000
This is a MIME encoded message
--raeburn1170172311
Content-Type: text/plain
raeburn Tue Jan 30 10:51:51 2007 EDT
Modified files:
/modules/raeburn monitor.pl
Log:
Additional optional fourth and fifth arguments (loadbal and sso) accepted by command line invocation of monitor.pl
If loadbal, script parses switch server page returned after log-in and performs roles initialization, course initialization and logout on the destination server.
If sso, log-in is against the SSO log-in page, and success is checked against the expected redirect destination. Roles selection, course initialization and logout follow on appropriate LON-CAPA server (depends on loadbal).
--raeburn1170172311
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20070130105151.txt"
Index: modules/raeburn/monitor.pl
diff -u modules/raeburn/monitor.pl:1.4 modules/raeburn/monitor.pl:1.5
--- modules/raeburn/monitor.pl:1.4 Mon Jul 11 16:37:26 2005
+++ modules/raeburn/monitor.pl Tue Jan 30 10:51:49 2007
@@ -11,6 +11,7 @@
# monitor.pl
#
# Stuart Raeburn, March 28, 2005
+# updated January 28, 2007
#
#########################################################
#
@@ -70,7 +71,7 @@
# of any of the failure types occurred; whereas itds@msu.edu would only be
# contacted if a conlost failure (no response from web server) occurred.
#
-# The failure types are as follows:
+# The failure types for non-SSO servers are as follows:
# conlost - Request for login page times out ( > 20s) or returns error
# unavailable - Server is unable to talk to itself via lonc/lond.
# missingparam - Log-in page returned too few parameters -- 4 required
@@ -84,6 +85,21 @@
# error (401,403,404,500 etc.)
# logoutfailed - attempt to logout after loading navmap failed.
#
+# For the SSO server at MSU, the following failure modified types apply:
+# conlost - Request for login page times out ( > 20s) or returns error
+# missingparam - Log-in page returned too few parameters -- 2 required
+# name of password form element, EncryptedStamp
+# unauthenticated - authentication failed -- response from Sentinel server
+# was not a redirect.
+# invalidcookie - (a) redirect was not to welcome page;
+# (b) lonID cookie could not be extracted from HTTP header
+# nologin - attempt to login resulted in error (401,403,404,500 etc.)
+# uninitialized - attempt to enter a course failed - could not initialize
+# rolesfailed - attempt to enter a course resulted in error (401,403,404,500)
+# navmapfailed - attempt to retrieve navmap failed - e.g., took > 60 s, or
+# error (401,403,404,500 etc.)
+# logoutfailed - attempt to logout after loading navmap failed.
+#
# Page requests are submitted sequentially. When a failure occurs, requests
# stop and actions are carried out for the specific failure type.
#
@@ -96,11 +112,32 @@
# times and add to MRTG monitoring as a means of tracking server performance
# under different load conditions.
#
-# This script itself should be called with three parameters:
+# This script itself should be called with a minimum of three parameters:
# encryption key (key used for DES encryption of the auto-user's password),
# hostname of the server (e.g., s1.lite.ms.edu),
# server nickname (e.g., s1).
-
+#
+# An optional fourth parameter is included if the log-in server is acting as
+# a loadbalancer, in which case, switch to a different server occurs after
+# successful authentication
+#
+# An optional fifth parameter is included if SSO is being used
+# In this case two additional files must exist: ssoconfig.txt & ssourls.txt
+#
+# The format of these files is as follows:
+# - ssoconfig.txt (Content POSTed to SSO log-in page)
+# (a) Fixed fields (no screen-scraped data required)
+# name=value
+# (b) Variable fields (either element name or value screen-scraped)
+# field:name=value (name and/or value may be blank)
+# examples are - username:AlternateID=username, password:=password
+# username field - name is AlternateID
+# password field - name must be screen-scraped
+#
+# - ssourls.txt (URLs for login, and expected redirect when authenticated)
+# login=https://login.msu.edu/AppLogin.Asp
+# redirect=https://login.msu.edu/AppWelcome.Asp?
+#
########################################################
# Configuration
#
@@ -108,6 +145,8 @@
#my $path_to_java = '/usr/java/j2sdk1.4.2_05/bin/java';
my $path_to_java = '/usr/java/j2re1.4.2_07/bin/java';
my $contact_email = 'helpdesk@loncapa.org';
+my $ssoconfig = $monitordir.'/ssoconfig.txt';
+my $ssourls = $monitordir.'/ssourls.txt';
#
#########################################################
@@ -117,13 +156,43 @@
my ($uname,$udom,$role,$upass);
if (@ARGV < 3) {
- print "Usage: $0 - encryptionkey hostname servernickname\n";
+ print "Usage: $0 - encryptionkey hostname servernickname [loadbal] [sso]\n";
exit(0);
}
my $keyphrase = $ARGV[0];
my $server = $ARGV[1];
my $serveralias = $ARGV[2];
+my $loadbalance = $ARGV[3];
+my $sso = $ARGV[4];
+my (%ssoparam,%ssourl,%recipients,@formitems);
+if ($sso) {
+ if (open (my $fh, "<$ssoconfig") ) {
+ my @buffer = <$fh>;
+ chomp(@buffer);
+ foreach my $line (@buffer) {
+ if ($line =~ /:/) {
+ my ($field,$what) = split(/:/,$line);
+ my ($name,$value) = split(/=/,$what);
+ if ($value eq '') {
+ push(@formitems,$field);
+ }
+ $ssoparam{'variable'}{$field} = {$name => $value};
+ } else {
+ my ($name,$value) = split(/=/,$line);
+ push(@{$ssoparam{'fixed'}},$name => $value);
+ }
+ }
+ }
+ if (open (my $fh, "<$ssourls") ) {
+ my @buffer = <$fh>;
+ chomp(@buffer);
+ foreach my $line (@buffer) {
+ my ($name,$value) = split(/=/,$line);
+ $ssourl{$name} = $value;
+ }
+ }
+}
my $authkeyfile = $monitordir.'/'.$serveralias.'/autouser.dat';
if (open (my $fh, "<$authkeyfile") ) {
@@ -133,7 +202,6 @@
$upass = $cipher->decrypt_hex($upass);
close($fh);
}
-my %recipients = ();
if (open (my $fh, "<$monitordir/$serveralias/recipients")) {
my @buffer = <$fh>;
close($fh);
@@ -148,10 +216,13 @@
}
}
-my @formitems = ('lextkey','uextkey','logtoken','serverid');
+if (!$sso) {
+ @formitems = ('lextkey','uextkey','logtoken','serverid');
+}
+
my %formvalues = ();
my %loadtimes = ();
-my ($outcome,$loginpage,$lonid);
+my ($outcome,$loginpage,$lonid,$loadbalserver);
my @failures = ('conlost','unavailable','missingparam','unauthenticated','invalidcookie','nologin','uninitialized','rolesfailed','navmapfailed','logoutfailed');
if (!-e "$monitordir/$serveralias") {
@@ -166,7 +237,7 @@
my $logfile = $monitordir.'/'.$serveralias.'/log';
-$outcome = &attempt_access($outcome,$loginpage,$lonid,$role,\@formitems,\%formvalues,\%loadtimes,$monitordir,$path_to_java);
+($outcome,$loadbalserver) = &attempt_access($outcome,$server,$loginpage,$lonid,$role,\@formitems,\%formvalues,\%loadtimes,$monitordir,$path_to_java,$loncookie_file,$loadbalance,$sso,\%ssoparam,\%ssourl);
my $mailflag = &alertstatus($outcome,$server,$serveralias,\@failures,$logfile,$monitordir);
if ($mailflag) {
my $mailresult = &mailalert($server,$outcome,$contact_email,\%recipients);
@@ -175,16 +246,39 @@
close($logfh);
}
+open(TIMES,">>$monitordir/$serveralias/times");
+my $serverid;
+if ($sso) {
+ print TIMES localtime(time)." sso $loadtimes{'sso'}\n";
+}
+if ($loadbalance) {
+ $serverid = ' '.$loadbalserver;
+}
+foreach my $key ('login','initialize','navmap') {
+ print TIMES localtime(time).' '.$key.' '.$loadtimes{$key}.$serverid."\n";
+}
+close(TIMES);
+
sub attempt_access {
- my ($outcome,$loginpage,$lonid,$role,$formitems,$formvalues,$loadtimes,$monitordir,$path_to_java) = @_;
- ($outcome,$loginpage) = &get_loginpage($server,$ua,$loadtimes);
+ my ($outcome,$server,$loginpage,$lonid,$role,$formitems,$formvalues,
+ $loadtimes,$monitordir,$path_to_java,$loncookie_file,$loadbalance,
+ $sso,$ssoparamref,$ssourlref) = @_;
+ ($outcome,$loginpage) = &get_loginpage($server,$ua,$loncookie_file,$sso);
+ my $loadbalserver;
if ($outcome eq 'ok') {
$ua->timeout(30);
if ($loginpage) {
- $outcome = &parse_loginpage($loginpage,$formitems,$formvalues);
+ $outcome = &parse_loginpage($loginpage,$formitems,$formvalues,$sso);
if ($outcome eq 'ok') {
- ($outcome,$lonid) = &logmein($server,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$path_to_java);
+ if ($sso) {
+ ($outcome,$lonid) = &ssologmein(\$server,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$loadbalance,$ssoparamref,$ssourlref);
+ } else {
+ ($outcome,$lonid) = &logmein(\$server,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$path_to_java,$loadbalance);
+ }
if ($outcome eq 'ok') {
+ if ($loadbalance) {
+ ($loadbalserver) = ($server =~ /^(s\d+)\./);
+ }
if ($lonid) {
&setcookie($loncookie_file,$lonid,$server);
$outcome = &pickrole($server,$loncookie_file,$ua,$role,$loadtimes);
@@ -204,20 +298,27 @@
}
}
}
- return $outcome;
+ return ($outcome,$loadbalserver);
}
sub get_loginpage {
- my ($server,$ua) = @_;
+ my ($server,$ua,$loncookie_file,$sso) = @_;
my ($outcome,$loginpage);
- my $URL = 'http://'.$server.'/adm/login';
+ my $URL = 'http://'.$server.'/';
+ if ($sso) {
+ $URL .= 'adm/roles';
+ } else {
+ $URL .= 'adm/login';
+ }
my $request = new HTTP::Request;
$request = GET $URL;
+ $ua->cookie_jar( $loncookie_file );
my $res = $ua->request($request);
if ($res->is_success) {
my $dump = $res->content;
$outcome = 'ok';
$loginpage = $dump;
+ $loncookie_file->extract_cookies($res);
} else {
$outcome = 'conlost';
}
@@ -225,28 +326,40 @@
}
sub parse_loginpage {
- my ($loginpage,$formitems,$formvalues) = @_;
+ my ($loginpage,$formitems,$formvalues,$sso) = @_;
my $outcome = 0;
my $paramcount = 0;
- if ($loginpage =~ m-src="/adm/lonKaputt/lonlogo_broken\.gif"-) {
- $outcome = "unavailable";
- } else {
- foreach (@{$formitems}) {
- if ($loginpage =~ m/<input\stype="hidden"\sname="$_"\svalue="(-?\w+)"\s?\/?>/) {
- $$formvalues{$_} = $1;
- $paramcount ++;
- }
+ if ($sso) {
+ if ($loginpage =~ m/<input\sname="([^"]+)"\stype="password"\svalue=""\ssize="10"\smaxlength="64"\s\/>/) {
+ $formvalues->{'password'} = $1;
+ $paramcount ++;
}
- if ($paramcount == scalar(@formitems)) {
- $outcome = 'ok';
+ if ($loginpage =~ m/<input\sname="EncryptedStamp"\svalue="([^"]+)"\stype="hidden"\s\/>/) {
+ $formvalues->{'EncryptedStamp'} = $1;
+ $paramcount ++;
+ }
+ } else {
+ if ($loginpage =~ m-src="/adm/lonKaputt/lonlogo_broken\.gif"-) {
+ $outcome = "unavailable";
} else {
- $outcome = 'missingparam';
+ foreach my $item (@{$formitems}) {
+ if ($loginpage =~ m/<input\stype="hidden"\sname="\Q$item\E"\svalue="(-?\w+)"\s?\/?>/) {
+ $formvalues->{$item} = $1;
+ $paramcount ++;
+ }
+ }
}
}
+ if ($paramcount == scalar(@{$formitems})) {
+ $outcome = 'ok';
+ } else {
+ $outcome = 'missingparam';
+ }
return $outcome;
}
+
sub logmein {
- my ($server,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$path_to_java) = @_;
+ my ($serverref,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$path_to_java,$loadbalance) = @_;
my ($outcome,$lonid);
my $upass0 = substr($upass,0,15);
my $upass1 = substr($upass,15,15);
@@ -262,7 +375,7 @@
open(PIPE,"-|") || exec "$path_to_java -classpath $classpath org.mozilla.javascript.tools.shell.Main $londesfile $$formvalues{uextkey} $$formvalues{lextkey} $upass2";
my $cryppass2 = <PIPE>;
close PIPE;
- my $URL = 'http://'.$server.'/adm/authenticate';
+ my $URL = 'http://'.$$serverref.'/adm/authenticate';
$ua->cookie_jar( $loncookie_file );
my $req = POST $URL,
Content_Type => 'application/x-www-form-urlencoded',
@@ -284,6 +397,15 @@
$outcome = 'missingparam';
} elsif ($dump =~ m-Username\sand\/or\spassword\scould\snot\sbe\sauthenticated-) {
$outcome = 'unauthenticated';
+ } elsif ($loadbalance) {
+ (my $switchurl,$$serverref) = &parse_switchpage($res->content,
+ $uname,$udom);
+ if ($switchurl) {
+ ($outcome,$lonid) =
+ &switchserver($switchurl,$ua,$loadtimes,$loncookie_file);
+ } else {
+ $outcome = 'nologin';
+ }
} else {
$loncookie_file->extract_cookies($res);
my $cookie = $loncookie_file->as_string;
@@ -303,6 +425,140 @@
return ($outcome,$lonid);
}
+sub ssologmein {
+ my ($serverref,$uname,$udom,$upass,$ua,$loncookie_file,$formvalues,$loadtimes,$monitordir,$loadbalance,$ssoparamref,$ssourlref) = @_;
+ my ($outcome,$lonid);
+ my $URL = $ssourlref->{'login'};
+ $ua->cookie_jar( $loncookie_file );
+ my $content;
+ if (ref($ssoparamref->{'fixed'}) eq 'ARRAY') {
+ $content = $ssoparamref->{'fixed'};
+ }
+ if (ref($ssoparamref->{'variable'}) eq 'HASH') {
+ foreach my $field (keys(%{$ssoparamref->{'variable'}})) {
+ if ($field eq 'password') {
+ my $passfield;
+ if ($formvalues->{'password'} ne '') {
+ $passfield = $formvalues->{'password'};
+ } else {
+ my @items = keys(%{$$ssoparamref{'variable'}{'password'}});
+ $passfield = $items[0];
+ }
+ push(@{$content},$passfield => $upass);
+ } elsif ($field eq 'username') {
+ my $userfield;
+ if ($formvalues->{'username'} ne '') {
+ $userfield = $formvalues->{'username'};
+ } else {
+ my @items = keys(%{$$ssoparamref{'variable'}{'username'}});
+ $userfield = $items[0];
+ }
+ push(@{$content},$userfield => $uname);
+ } else {
+ my @items = keys(%{$$ssoparamref{'variable'}{$field}});
+ my $fieldname = $items[0];
+ my $fieldvalue;
+ if ($formvalues->{$field} ne '') {
+ $fieldvalue = $formvalues->{$field};
+ }
+ push (@{$content},$fieldname => $fieldvalue);
+ }
+ }
+ }
+ my $req = POST $URL,
+ Content_Type => 'application/x-www-form-urlencoded',
+ Content => $content;
+ $loncookie_file->add_cookie_header($req);
+ my $start = [gettimeofday];
+ my $res = $ua->request($req);
+ $loncookie_file->extract_cookies($res);
+ my $cookie = $loncookie_file->as_string;
+ if ($res->is_redirect()) {
+ my $newurl = $res->header( "location" );
+ if ($newurl eq $ssourlref->{'redirect'}) {
+ my $end = [gettimeofday];
+ $loadtimes{sso} = tv_interval $start, $end;
+ $loadtimes{sso} *= 1000;
+ ($outcome,$lonid) =
+ &welcomepage($ua,$newurl,$serverref,$loncookie_file,$uname,$udom,$loadtimes,$monitordir);
+ } else {
+ $outcome = 'invalidcookie';
+ }
+ } else {
+ $outcome = 'unauthenticated';
+ }
+ return ($outcome,$lonid);
+}
+
+sub welcomepage {
+ my ($ua,$newurl,$serverref,$loncookie_file,$uname,$udom,$loadtimes,$monitordir) = @_;
+ my ($outcome,$lonid);
+ my $start = [gettimeofday];
+ my $request = GET $newurl;
+ $loncookie_file->add_cookie_header($request);
+ my $response = $ua->request($request);
+ if ($response->is_success) {
+ if ($loadbalance) {
+ (my $switchurl,$$serverref) = &parse_switchpage($response->content,
+ $uname,$udom);
+ if ($switchurl) {
+ ($outcome,$lonid) =
+ &switchserver($switchurl,$ua,$loadtimes,$loncookie_file);
+ } else {
+ $outcome = 'nologin';
+ }
+ } else {
+ $loncookie_file->extract_cookies($response);
+ my $cookie = $loncookie_file->as_string;
+ if ($cookie =~ m/lonID=(\w+);/) {
+ $lonid = $1;
+ $outcome = 'ok';
+ } else {
+ $outcome = 'invalidcookie';
+ }
+ }
+ } else {
+ $outcome = 'nologin';
+ }
+ my $end = [gettimeofday];
+ $loadtimes{login} = tv_interval $start, $end;
+ $loadtimes{login} *= 1000;
+ return ($outcome,$lonid);
+}
+
+sub parse_switchpage {
+ my ($content,$uname,$udom) = @_;
+ if ($content =~ m{<meta\sHTTP\-EQUIV="Refresh"\sCONTENT="0\.5;\surl=(http://)([^/]+)(/adm/login\?domain=\Q$udom\E&username=\Q$uname\E&token=\w+)">}s ) {
+ return ($1.$2.$3, $2);
+ }
+ return;
+}
+
+sub switchserver {
+ my ($url,$ua,$loadtimes,$loncookie_file) = @_;
+ my ($outcome,$lonid);
+ my $request = GET $url;
+ $loncookie_file->add_cookie_header($request);
+ my $res = $ua->request($request);
+ my $start = [gettimeofday];
+ if ($res->is_success) {
+ $loncookie_file->extract_cookies($res);
+ my $cookie = $loncookie_file->as_string;
+ if ($cookie =~ m/lonID=(\w+);/) {
+ $lonid = $1;
+ $outcome = 'ok';
+ } else {
+ $outcome = 'invalidcookie';
+ }
+ my $end = [gettimeofday];
+ $loadtimes{login} = tv_interval $start, $end;
+ $loadtimes{login} *= 1000;
+ } else {
+ $outcome = 'nologin';
+ }
+ return ($outcome,$lonid);
+}
+
sub pickrole {
my ($server,$loncookie_file,$ua,$role,$loadtimes) = @_;
my ($start,$end,$outcome);
--raeburn1170172311--