File:  [LON-CAPA] / loncom / Attic / lonManage
Revision 1.16: download - view: text, annotated - select for diffs
Tue Oct 21 09:44:04 2003 UTC (20 years, 8 months ago) by foxr
Branches: MAIN
CVS tags: HEAD
Add ability to manage entire cluster in hosts table.

    1: #!/usr/bin/perl
    2: # The LearningOnline Network with CAPA
    3: #
    4: #  lonManage supports remote management of nodes in a LonCAPA cluster.
    5: #
    6: #  $Id: lonManage,v 1.16 2003/10/21 09:44:04 foxr Exp $
    7: #
    8: # $Id: lonManage,v 1.16 2003/10/21 09:44:04 foxr Exp $
    9: #
   10: # Copyright Michigan State University Board of Trustees
   11: #
   12: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   13: ## LON-CAPA is free software; you can redistribute it and/or modify
   14: # it under the terms of the GNU General Public License as published by
   15: # the Free Software Foundation; either version 2 of the License, or
   16: # (at your option) any later version.
   17: #
   18: # LON-CAPA is distributed in the hope that it will be useful,
   19: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   20: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21: # GNU General Public License for more details.
   22: #
   23: # You should have received a copy of the GNU General Public License
   24: # along with LON-CAPA; if not, write to the Free Software
   25: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   26: #
   27: # /home/httpd/html/adm/gpl.txt
   28: #
   29: # http://www.lon-capa.org/
   30: #
   31: #
   32: #   lonManage supports management of remot nodes in a lonCAPA cluster.
   33: #   it is a command line tool.  The following command line syntax (usage)
   34: #   is supported:
   35: #
   36: #    lonManage  -push   <tablename>  newfile  [host]
   37: #        Push <tablename> to the lonTabs directory.  Note that
   38: #        <tablename> must be one of:
   39: #           host  (hosts.tab)
   40: #           domain (domain.tab)
   41: #
   42: #    lonManage  -reinit lonc [host]
   43: #           Sends a HUP signal to the remote systems's lond.
   44: #
   45: #    lonmanage  -reinit lond [host]
   46: #          Requests the remote system's lond perform the same action as if
   47: #          it had received a HUP signal.
   48: #
   49: #    In the above syntax, the host above is the hosts.tab name of a host,
   50: #    not the IP address of the host
   51: #  
   52: #   If [host] is not supplied, every host in the client's hosts.tab
   53: #   table is iterated through and procesed..
   54: #
   55: #  $Log: lonManage,v $
   56: #  Revision 1.16  2003/10/21 09:44:04  foxr
   57: #  Add ability to manage entire cluster in hosts table.
   58: #
   59: #  Revision 1.15  2003/09/16 09:49:54  foxr
   60: #  Adjust the usage message to reflect what actually will happen on
   61: #  --reinit={lond|lonc}
   62: #
   63: #  Revision 1.14  2003/09/08 09:45:20  foxr
   64: #  Remove BUGBUG about comment about authentication as we'll be doing
   65: #  host based authentication initially (no need for lonManage to do anything),
   66: #  and certificate based later (need at that time).
   67: #
   68: #  Revision 1.13  2003/08/19 10:26:24  foxr
   69: #  Initial working version... tested against an unmodified lond this
   70: #  produces an unknown_cmd response which is about what I'd expect.
   71: #
   72: #  Revision 1.12  2003/08/18 11:08:07  foxr
   73: #  Debug request building in Transact.
   74: #
   75: #  Revision 1.11  2003/08/18 10:45:32  foxr
   76: #  Felt strongly enough about hoisting ReadConfiguration into a separate sub
   77: #  that I did it now before I forgot.
   78: #
   79: #  Revision 1.10  2003/08/18 10:43:31  foxr
   80: #  Code/test ValidHost.  The hosts.tab and the perl variables are read in as
   81: #  global hashes as a side effect.  May later want to clean this up by making
   82: #  a separate getconfig function and hoisting the config reads into that.
   83: #
   84: #  Revision 1.9  2003/08/18 10:25:46  foxr
   85: #  Write ReinitProcess function in terms of ValidHost and Transact.
   86: #
   87: #  Revision 1.8  2003/08/18 10:18:21  foxr
   88: #  Completed PushFile function in terms of
   89: #  - ValidHost - Determines if target host is valid.
   90: #  - Transact  - Performs one of the valid transactions with the
   91: #                appropriate lonc<-->lond client/server pairs.
   92: #
   93: #  Revision 1.7  2003/08/18 09:56:01  foxr
   94: #  1. Require to be run as root.
   95: #  2. Catch case where no operation switch is supplied and put out usage.
   96: #  3. skeleton/comments for PushFile function.
   97: #
   98: #  Revision 1.6  2003/08/12 11:02:59  foxr
   99: #  Implement command switch dispatching.
  100: #
  101: #  Revision 1.5  2003/08/12 10:55:42  foxr
  102: #  Complete command line parsing (tested)
  103: #
  104: #  Revision 1.4  2003/08/12 10:40:44  foxr
  105: #  Get switch parsing right.
  106: #
  107: #  Revision 1.3  2003/08/12 10:22:35  foxr
  108: #  Put in parameter parsing infrastructure
  109: #
  110: #  Revision 1.2  2003/08/12 09:58:49  foxr
  111: #  Add usage and skeleton documentation.
  112: #
  113: #
  114: 
  115: 
  116: 
  117: # Modules required:
  118: 
  119: use strict;			# Because it's good practice.
  120: use English;			# Cause I like meaningful names.
  121: use Getopt::Long;
  122: use LONCAPA::Configuration;	# To handle configuration I/O.
  123: use IO::Socket::UNIX;		# To communicate with lonc.
  124: 
  125: # File scoped variables:
  126: 
  127: my %perlvar;			# Perl variable defs from apache config.
  128: my %hostshash;			# Host table as a host indexed hash.
  129: 
  130: #
  131: #   prints out utility's command usage info.
  132: #
  133: sub Usage  {
  134:     print "Usage:";
  135:     print <<USAGE;
  136:     lonManage  --push=<tablename>  newfile  [host]
  137:         Push <tablename> to the lonTabs directory.  Note that
  138:         <tablename> must be one of:
  139:            host  (hosts.tab)
  140:            domain (domain.tab)
  141: 
  142:     lonManage  --reinit=lonc [host]
  143:        Causes lonc in the remote system to reread hosts.tab and
  144:        adjust the set of clients that are being maintained to match
  145:        the new file.
  146:        
  147: 
  148:     lonManage  --reinit=lond [host]
  149:        Causes lond in the remote system to reread the hosts.tab file
  150:        and adjust the set of servers to match changes in that file.
  151: 
  152:     In the above syntax, the host above is the hosts.tab name of a host,
  153:     not the IP address of the host.
  154: 
  155:     If [host] is omitted, all hosts in the hosts.tab file are iterated
  156:     over.
  157: 
  158: USAGE
  159: 
  160: 
  161: }
  162: #
  163: #   Lifted from lonnet.pm - and we need to figure out a way to get it back in.
  164: #   Performas a transaction with lond via the lonc proxy server.
  165: #   Parameter:
  166: #      cmd  - The text of the request.
  167: #      host - The host to which the request ultimately goes.
  168: #   Returns:
  169: #      The text of the reply from the lond or con_lost if not able to contact
  170: #      lond/lonc etc.
  171: #
  172: sub subreply {
  173:     my ($cmd,$server)=@_;
  174:     my $peerfile="$perlvar{'lonSockDir'}/$server";
  175:     my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
  176:                                      Type    => SOCK_STREAM,
  177:                                      Timeout => 10)
  178:        or return "con_lost";
  179:     print $client "$cmd\n";
  180:     my $answer=<$client>;
  181:     if (!$answer) { $answer="con_lost"; }
  182:     chomp($answer);
  183:     return $answer;
  184: }
  185: #   >>> BUGBUG <<< 
  186: #
  187: #  Use Getopt::Long to parse the parameters of the program.
  188: #
  189: #  Return value is a list consisting of:
  190: #    A 'command' which is one of:
  191: #       push   - table push requested.
  192: #       reinit - reinit requested.
  193: #   Additional parameters as follows:
  194: #       for push: Tablename, hostname
  195: #       for reinit: Appname  hostname
  196: #
  197: #   This function does not validation of the parameters of push and
  198: #   reinit.
  199: #
  200: #   returns a list.  The first element of the list is the operation name
  201: #   (e.g. reinit or push).  The second element is the switch parameter.
  202: #   for push, this is the table name, for reinit, this is the process name.
  203: #   Additional elements of the list are the command argument.  The count of
  204: #   command arguments is validated, but not their semantics.
  205: #
  206: #   returns an empty list if the parse fails.
  207: #
  208: 
  209: sub ParseArgs {
  210:     my $pushing   = '';
  211:     my $reinitting = '';
  212: 
  213:     if(!GetOptions('push=s'    => \$pushing,
  214: 	           'reinit=s'  => \$reinitting)) {
  215: 	return ();
  216:     }
  217: 
  218:     #  Require exactly   one of --push and --reinit
  219: 
  220:     my $command    = '';
  221:     my $commandarg = '';
  222:     my $paramcount = @ARGV; 	# Number of additional arguments.
  223:     
  224: 
  225:     if($pushing ne '') {
  226: 
  227:         # --push takes in addition a table, and an optional  host:
  228:         #
  229: 	if(($paramcount != 2) && ($paramcount != 1)) {
  230: 	    return ();		# Invalid parameter count.
  231: 	}
  232: 	if($command ne '') {
  233: 	    return ();
  234: 	} else {
  235: 	    
  236: 	    $command    = 'push';
  237: 	    $commandarg = $pushing;
  238: 	}
  239:     }
  240: 
  241:     if ($reinitting ne '') {
  242: 
  243: 	# --reinit takes in addition just an optional  host name
  244: 
  245: 	if($paramcount > 1) {
  246: 	    return ();
  247: 	}
  248: 	if($command ne '') {
  249: 	    return ();
  250: 	} else {
  251: 	    $command    = 'reinit';
  252: 	    $commandarg = $reinitting; 
  253: 	}
  254:     }
  255: 
  256:     #  Build the result list:
  257: 
  258:     my @result = ($command, $commandarg);
  259:     my $i;
  260:     for($i = 0; $i < $paramcount; $i++) {
  261: 	push(@result, $ARGV[$i]);
  262:     }
  263:     
  264:     return @result;
  265: }
  266: #
  267: #  Read the loncapa configuration stuff.
  268: #
  269: sub ReadConfig {
  270:     my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf');
  271:     %perlvar       = %{$perlvarref};
  272:     my $hoststab   = LONCAPA::Configuration::read_hosts(
  273: 					"$perlvar{'lonTabDir'}/hosts.tab");
  274:     %hostshash     = %{$hoststab};
  275: 
  276: }
  277: #
  278: #  Determine if the target host is valid.
  279: #  This is done by reading the current hosts.tab file.
  280: #  For the host to be valid, it must be inthe file.
  281: #
  282: #  Parameters:
  283: #     host   - Name of host to check on.
  284: #  Returns:
  285: #     true   if host is valid.
  286: #     false  if host is invalid.
  287: #
  288: sub ValidHost {
  289:     my $host       = shift;
  290:    
  291: 
  292:     return defined $hostshash{$host};
  293: 
  294: }
  295: 
  296: 
  297: 
  298: #
  299: #  Performs a transaction with lonc.
  300: #  By the time this is called, the transaction has already been
  301: #  validated by the caller.
  302: #
  303: #   Parameters:
  304: #
  305: #   host    - hosts.tab name of the host whose lonc we'll be talking to.
  306: #   command - The base command we'll be asking lond to execute.
  307: #   body    - [optional] If supplied, this is a command body that is a ref.
  308: #             to an array of lines that will be appended to the 
  309: #             command.
  310: #
  311: #  NOTE:
  312: #    The command will be done as an encrypted operation.
  313: #
  314: sub Transact {
  315:     my $host    = shift;
  316:     my $command = shift;
  317:     my $haveBody= 0;
  318:     my $body;
  319:     my $i;
  320: 
  321:     if(scalar @ARG) {
  322: 	$body = shift;
  323: 	$haveBody = 1;
  324:     }
  325:     #  Construct the command to send to the server:
  326:     
  327:     my $request = "encrypt\:";	# All requests are encrypted.
  328:     $request   .= $command;
  329:     if($haveBody) {
  330: 	$request .= "\:";
  331: 	my $bodylines = scalar @$body;
  332: 	for($i = 0; $i < $bodylines; $i++) {
  333: 	    $request .= $$body[$i];
  334: 	}
  335:     } else {
  336: 	$request .= "\n";
  337:     }
  338:     # Body is now built... transact with lond..
  339:     
  340:     my $answer = subreply($request, $host);
  341: 
  342:     print "$answer\n";
  343: 
  344: }
  345: #
  346: #   Called to push a file to the remote system.
  347: #   The only legal files to push are hosts.tab and domain.tab.
  348: #   Security is somewhat improved by
  349: #   
  350: #   - Requiring the user run as root.
  351: #   - Connecting with lonc rather than lond directly ensuring this is a loncapa
  352: #     host
  353: #   - We must appear in the remote host's hosts.tab file.
  354: #   - The host must appear in our hosts.tab file.
  355: #
  356: #  Parameters:
  357: #     tablename - must be one of hosts or domain.
  358: #     tablefile - name of the file containing the table to push.
  359: #     host      - name of the host to push this file to.     
  360: #
  361: #    >>>BUGBUG<<< This belongs in lonnet.pm.
  362: #
  363: sub PushFile {
  364:     my $tablename = shift;
  365:     my $tablefile = shift;
  366:     my $host      = shift;
  367:     
  368:     # Open the table file:
  369: 
  370:     if(!open(TABLEFILE, "<$tablefile")) {
  371: 	die "ENOENT - No such file or directory $tablefile";
  372:     }
  373:   
  374:     # Require that the host be valid:
  375: 
  376:     if(!ValidHost($host)) {
  377: 	die "EHOSTINVAL - Invalid host $host"; # Ok so I invented this 'errno'.
  378:     }
  379:     # Read in the file.  If the table name is valid, push it.
  380: 
  381:     my @table = <TABLEFILE>;	#  These files are pretty small.
  382:     close TABLEFILE;
  383: 
  384:     if( ($tablename eq "host")    ||
  385: 	($tablename eq "domain")) {
  386: 	print("Pushing $tablename to $host\n");
  387: 	Transact($host, "pushfile:$tablename",\@table);
  388:     } else {
  389: 	die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain";
  390:     }
  391: }
  392: #
  393: #   This function is called to reinitialize a server in a remote host.
  394: #   The servers that can be reinitialized are:
  395: #   - lonc   - The lonc client process.
  396: #   - lond   - The lond daemon.
  397: #  NOTE:
  398: #    Reinitialization in this case means re-scanning the hosts table,
  399: #    starting new lond/lonc's as approprate and stopping existing lonc/lond's.
  400: #
  401: #  Parameters:
  402: #     process - The name of the process to reinit (lonc or lond).
  403: #     host    - The host in which this reinit will happen.
  404: #
  405: #   >>>BUGBUG<<<< This belongs  in lonnet.pm
  406: #
  407: sub ReinitProcess {
  408:     my $process = shift;
  409:     my $host    = shift;
  410: 
  411:     #  Ensure the host is valid:
  412:     
  413:     if(!ValidHost($host)) {
  414: 	die "EHOSTINVAL - Invalid host $host";
  415:     }
  416:     # Ensure target process selector is valid:
  417: 
  418:     if(($process eq "lonc") ||
  419:        ($process eq "lond")) {
  420: 	print("Reinitializing $process in $host\n");
  421: 	Transact($host, "reinit:$process");
  422:     } else {
  423: 	die "EINVAL -Invalid parameter. Process $process must be lonc or lond";
  424:     }
  425: }
  426: #--------------------------- Entry point: --------------------------
  427: 
  428: ReadConfig;			# Read the configuration info (incl.hosts).
  429: 
  430: 
  431: #  Parse the parameters
  432: #  If command parsing failed, then print usage:
  433: 
  434: my @params   = ParseArgs;
  435: my $nparam   = @params;
  436: 
  437: if($nparam == 0) {
  438:     Usage;
  439:     exit -1;
  440: }
  441: #
  442: #   Next, ensure we are running as EID root.
  443: #
  444: if ($EUID != 0) {
  445:     die "ENOPRIV - No privilege for requested operation"
  446: }
  447: 
  448: 
  449: #   Based on the operation requested invoke the appropriate function:
  450: 
  451: my $operation = shift @params;
  452: 
  453: if($operation eq "push") {  # push tablename filename host
  454:     my $tablename = shift @params;
  455:     my $tablefile = shift @params;
  456:     my $host      = shift @params;
  457:     if($host) {
  458: 	PushFile($tablename, $tablefile, $host);
  459:     } else {			# Push to whole cluster.
  460: 	foreach my $host (keys %hostshash) {
  461: 	    PushFile($tablename, $tablefile, $host);
  462: 	}
  463:     }
  464: 
  465: } elsif($operation eq "reinit") {	# reinit processname host.
  466:     my $process   = shift @params;
  467:     my $host      = shift @params;
  468:     if ($host) {
  469: 	ReinitProcess($process, $host);
  470:     } else {			# Reinit whole cluster.
  471: 	foreach my $host (keys %hostshash) {
  472: 	    ReinitProcess($process,$host);
  473: 	}
  474:     }
  475: } 
  476: else {
  477:     Usage;
  478: }
  479: exit 0;
  480: 
  481: =head1 NAME
  482:     lonManage - Command line utility for remote management of lonCAPA
  483:     cluster nodes.
  484: 
  485: =head1 SYNOPSIS
  486: 
  487: Usage:
  488:     B<lonManage  --push=<tablename>  newfile  host>
  489:         Push <tablename> to the lonTabs directory.  Note that
  490:         <tablename> must be one of:
  491:            hosts  (hosts.tab)
  492:            domain (domain.tab)
  493: 
  494:     B<lonManage  --reinit=lonc host>
  495:            Sends a HUP signal to the remote systems's lond.
  496: 
  497:     B<lonmanage  --reinit=lond host>
  498:           Requests the remote system's lond perform the same action as if
  499:           it had received a HUP signal.
  500: 
  501:     In the above syntax, the host above is the hosts.tab name of a host,
  502:     not the IP address of the host.
  503: 
  504: 
  505: =head1 DESCRIPTION
  506: 
  507: =head1 PREREQUISITES
  508: 
  509: =item strict
  510: =item Getopt::Long
  511: =item English
  512: =item IO::Socket::UNIX
  513: 
  514: =head1 KEY Subroutines.
  515: 
  516: =head1  CATEGORIES
  517:     Command line utility
  518: 
  519: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.