Annotation of loncom/lonManage, revision 1.21
1.1 foxr 1: #!/usr/bin/perl
2: # The LearningOnline Network with CAPA
3: #
4: # lonManage supports remote management of nodes in a LonCAPA cluster.
5: #
1.21 ! foxr 6: # $Id: lonManage,v 1.20 2003/11/03 10:24:49 foxr Exp $
1.1 foxr 7: #
1.21 ! foxr 8: # $Id: lonManage,v 1.20 2003/11/03 10:24:49 foxr Exp $
1.1 foxr 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: #
1.16 foxr 36: # lonManage -push <tablename> newfile [host]
1.1 foxr 37: # Push <tablename> to the lonTabs directory. Note that
38: # <tablename> must be one of:
1.15 foxr 39: # host (hosts.tab)
1.1 foxr 40: # domain (domain.tab)
41: #
1.16 foxr 42: # lonManage -reinit lonc [host]
1.1 foxr 43: # Sends a HUP signal to the remote systems's lond.
44: #
1.16 foxr 45: # lonmanage -reinit lond [host]
1.1 foxr 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,
1.16 foxr 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..
1.1 foxr 54: #
1.3 foxr 55: #
1.10 foxr 56:
1.14 foxr 57:
1.13 foxr 58:
1.10 foxr 59: # Modules required:
60:
1.17 foxr 61: use lib ".";
62:
1.7 foxr 63: use strict; # Because it's good practice.
64: use English; # Cause I like meaningful names.
1.3 foxr 65: use Getopt::Long;
1.13 foxr 66: use IO::Socket::UNIX; # To communicate with lonc.
1.17 foxr 67: use LondConnection;
1.10 foxr 68:
69: # File scoped variables:
70:
71: my %perlvar; # Perl variable defs from apache config.
72: my %hostshash; # Host table as a host indexed hash.
1.2 foxr 73:
1.19 foxr 74: my $MyHost=""; # Host name to use as me.
75: my $ForeignHostTab=""; # Name of foreign hosts table.
1.21 ! foxr 76: my $ServerPort; # Port used to connect to lond.
1.18 foxr 77:
1.13 foxr 78: #
79: # prints out utility's command usage info.
80: #
1.3 foxr 81: sub Usage {
1.2 foxr 82: print "Usage:";
83: print <<USAGE;
1.18 foxr 84: lonManage [--myname=host --hosts=table] --push=<tablename> newfile [host]
1.2 foxr 85: Push <tablename> to the lonTabs directory. Note that
86: <tablename> must be one of:
1.15 foxr 87: host (hosts.tab)
1.2 foxr 88: domain (domain.tab)
89:
1.18 foxr 90: lonManage [--myname=host --hosts=table] --reinit=lonc [host]
1.15 foxr 91: Causes lonc in the remote system to reread hosts.tab and
92: adjust the set of clients that are being maintained to match
93: the new file.
94:
1.2 foxr 95:
1.18 foxr 96: lonManage [--myname=host --hosts=table] --reinit=lond [host]
1.15 foxr 97: Causes lond in the remote system to reread the hosts.tab file
98: and adjust the set of servers to match changes in that file.
1.2 foxr 99:
100: In the above syntax, the host above is the hosts.tab name of a host,
101: not the IP address of the host.
1.16 foxr 102:
103: If [host] is omitted, all hosts in the hosts.tab file are iterated
104: over.
105:
1.18 foxr 106: For all of the above syntaxes if --myname=host and --hosts=table are
107: supplied (both must be present), the utility runs in standalone mode
108: presenting itself to the world as 'host' and using the hosts.tab file
109: specified in the --hosts switch.
1.2 foxr 110: USAGE
111:
112:
113: }
1.21 ! foxr 114:
! 115: sub MakeLondConnection {
! 116: my $host = shift;
! 117: return "junk";
! 118: }
! 119:
! 120: sub NegotiateStartup {
! 121: my $connection = shift;
! 122:
! 123: return "ok";
! 124: }
! 125: sub PerformTransaction {
! 126: my $connection = shift;
! 127: my $command = shift;
! 128:
! 129: return "ok";
! 130: }
1.13 foxr 131: #
1.21 ! foxr 132: # Performs a transaction direct to a remote lond.
1.13 foxr 133: # Parameter:
134: # cmd - The text of the request.
135: # host - The host to which the request ultimately goes.
136: # Returns:
137: # The text of the reply from the lond or con_lost if not able to contact
138: # lond/lonc etc.
139: #
140: sub subreply {
1.21 ! foxr 141: my $cmd = shift;
! 142: my $host = shift;
! 143:
! 144:
! 145: my $connection = MakeLondConnection($host);
! 146: if ($connection eq undef) {
! 147: return "Connect Failed";
! 148: }
! 149: my $reply = NegotiateStartup($connection);
! 150: if($reply != "ok") {
! 151: return "connection negotiation failed";
! 152: }
! 153: my $reply = PerformTransaction($connection, $cmd);
! 154: return $reply;
! 155:
! 156:
! 157: # my ($cmd,$server)=@_;
! 158: # my $peerfile="$perlvar{'lonSockDir'}/$server";
! 159: # my $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
! 160: # Type => SOCK_STREAM,
! 161: # Timeout => 10)
! 162: # or return "con_lost";
! 163: # print $client "$cmd\n";
! 164: # my $answer=<$client>;
! 165: # if (!$answer) { $answer="con_lost"; }
! 166: # chomp($answer);
! 167: # return $answer;
1.13 foxr 168: }
169: # >>> BUGBUG <<<
1.2 foxr 170: #
1.3 foxr 171: # Use Getopt::Long to parse the parameters of the program.
172: #
173: # Return value is a list consisting of:
174: # A 'command' which is one of:
175: # push - table push requested.
176: # reinit - reinit requested.
177: # Additional parameters as follows:
178: # for push: Tablename, hostname
179: # for reinit: Appname hostname
180: #
181: # This function does not validation of the parameters of push and
182: # reinit.
1.4 foxr 183: #
184: # returns a list. The first element of the list is the operation name
185: # (e.g. reinit or push). The second element is the switch parameter.
186: # for push, this is the table name, for reinit, this is the process name.
187: # Additional elements of the list are the command argument. The count of
188: # command arguments is validated, but not their semantics.
189: #
1.3 foxr 190: # returns an empty list if the parse fails.
191: #
192:
1.18 foxr 193:
1.3 foxr 194: sub ParseArgs {
1.4 foxr 195: my $pushing = '';
1.7 foxr 196: my $reinitting = '';
1.5 foxr 197:
1.4 foxr 198: if(!GetOptions('push=s' => \$pushing,
1.18 foxr 199: 'reinit=s' => \$reinitting,
200: 'myname=s' => \$MyHost,
201: 'hosts=s' => \$ForeignHostTab)) {
202: return ();
203: }
204: # The --myname and --hosts switch must have values and
205: # most both appear if either appears:
206:
207: if(($MyHost ne "") && ($ForeignHostTab eq "")) {
208: return ();
209: }
210: if(($ForeignHostTab ne "") && ($MyHost eq "")) {
1.4 foxr 211: return ();
212: }
213:
214: # Require exactly one of --push and --reinit
215:
1.5 foxr 216: my $command = '';
1.4 foxr 217: my $commandarg = '';
1.5 foxr 218: my $paramcount = @ARGV; # Number of additional arguments.
219:
220:
1.4 foxr 221: if($pushing ne '') {
1.5 foxr 222:
1.16 foxr 223: # --push takes in addition a table, and an optional host:
1.5 foxr 224: #
1.16 foxr 225: if(($paramcount != 2) && ($paramcount != 1)) {
1.5 foxr 226: return (); # Invalid parameter count.
227: }
1.4 foxr 228: if($command ne '') {
229: return ();
230: } else {
1.5 foxr 231:
1.4 foxr 232: $command = 'push';
233: $commandarg = $pushing;
234: }
235: }
1.5 foxr 236:
1.4 foxr 237: if ($reinitting ne '') {
1.5 foxr 238:
1.16 foxr 239: # --reinit takes in addition just an optional host name
1.5 foxr 240:
1.16 foxr 241: if($paramcount > 1) {
1.5 foxr 242: return ();
243: }
1.4 foxr 244: if($command ne '') {
245: return ();
246: } else {
247: $command = 'reinit';
248: $commandarg = $reinitting;
249: }
250: }
251:
1.5 foxr 252: # Build the result list:
253:
254: my @result = ($command, $commandarg);
255: my $i;
256: for($i = 0; $i < $paramcount; $i++) {
257: push(@result, $ARGV[$i]);
258: }
259:
260: return @result;
1.3 foxr 261: }
1.10 foxr 262: #
1.19 foxr 263: # Read the loncapa configuration stuff. If ForeignHostTab is empty,
264: # assume we are part of a loncapa cluster and read the hosts.tab
265: # file from the config directory. Otherwise, ForeignHossTab
266: # is the name of an alternate configuration file to read in
267: # standalone mode.
1.11 foxr 268: #
269: sub ReadConfig {
1.19 foxr 270:
271: if($ForeignHostTab eq "") {
272: my $perlvarref = LondConnection::read_conf('loncapa.conf');
273: %perlvar = %{$perlvarref};
274: my $hoststab = LondConnection::read_hosts(
275: "$perlvar{'lonTabDir'}/hosts.tab");
276: %hostshash = %{$hoststab};
1.20 foxr 277: $MyHost = $perlvar{lonHostID}; # Set hostname from vars.
1.21 ! foxr 278: $ServerPort = $perlvar{londPort};
1.19 foxr 279: } else {
280: my $hoststab = LondConnection::read_hosts($ForeignHostTab);
281: %hostshash = %{$hoststab};
1.21 ! foxr 282: $ServerPort = 5663;
1.19 foxr 283: }
1.11 foxr 284:
285: }
286: #
1.10 foxr 287: # Determine if the target host is valid.
288: # This is done by reading the current hosts.tab file.
289: # For the host to be valid, it must be inthe file.
290: #
291: # Parameters:
292: # host - Name of host to check on.
293: # Returns:
294: # true if host is valid.
295: # false if host is invalid.
296: #
1.8 foxr 297: sub ValidHost {
1.10 foxr 298: my $host = shift;
1.11 foxr 299:
1.10 foxr 300:
301: return defined $hostshash{$host};
302:
1.8 foxr 303: }
1.13 foxr 304:
305:
306:
1.12 foxr 307: #
308: # Performs a transaction with lonc.
309: # By the time this is called, the transaction has already been
310: # validated by the caller.
311: #
312: # Parameters:
313: #
314: # host - hosts.tab name of the host whose lonc we'll be talking to.
315: # command - The base command we'll be asking lond to execute.
316: # body - [optional] If supplied, this is a command body that is a ref.
317: # to an array of lines that will be appended to the
318: # command.
319: #
320: # NOTE:
321: # The command will be done as an encrypted operation.
322: #
1.8 foxr 323: sub Transact {
1.12 foxr 324: my $host = shift;
325: my $command = shift;
326: my $haveBody= 0;
327: my $body;
328: my $i;
329:
330: if(scalar @ARG) {
331: $body = shift;
332: $haveBody = 1;
333: }
334: # Construct the command to send to the server:
335:
336: my $request = "encrypt\:"; # All requests are encrypted.
337: $request .= $command;
338: if($haveBody) {
339: $request .= "\:";
340: my $bodylines = scalar @$body;
341: for($i = 0; $i < $bodylines; $i++) {
342: $request .= $$body[$i];
343: }
344: } else {
345: $request .= "\n";
346: }
1.13 foxr 347: # Body is now built... transact with lond..
348:
349: my $answer = subreply($request, $host);
350:
351: print "$answer\n";
1.10 foxr 352:
1.8 foxr 353: }
1.7 foxr 354: #
355: # Called to push a file to the remote system.
356: # The only legal files to push are hosts.tab and domain.tab.
357: # Security is somewhat improved by
358: #
359: # - Requiring the user run as root.
360: # - Connecting with lonc rather than lond directly ensuring this is a loncapa
361: # host
362: # - We must appear in the remote host's hosts.tab file.
363: # - The host must appear in our hosts.tab file.
364: #
365: # Parameters:
366: # tablename - must be one of hosts or domain.
367: # tablefile - name of the file containing the table to push.
368: # host - name of the host to push this file to.
369: #
1.13 foxr 370: # >>>BUGBUG<<< This belongs in lonnet.pm.
371: #
1.7 foxr 372: sub PushFile {
373: my $tablename = shift;
374: my $tablefile = shift;
375: my $host = shift;
376:
1.8 foxr 377: # Open the table file:
378:
379: if(!open(TABLEFILE, "<$tablefile")) {
380: die "ENOENT - No such file or directory $tablefile";
381: }
382:
383: # Require that the host be valid:
384:
385: if(!ValidHost($host)) {
386: die "EHOSTINVAL - Invalid host $host"; # Ok so I invented this 'errno'.
387: }
388: # Read in the file. If the table name is valid, push it.
389:
390: my @table = <TABLEFILE>; # These files are pretty small.
391: close TABLEFILE;
392:
393: if( ($tablename eq "host") ||
394: ($tablename eq "domain")) {
1.16 foxr 395: print("Pushing $tablename to $host\n");
1.12 foxr 396: Transact($host, "pushfile:$tablename",\@table);
1.8 foxr 397: } else {
398: die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain";
399: }
1.7 foxr 400: }
1.9 foxr 401: #
402: # This function is called to reinitialize a server in a remote host.
403: # The servers that can be reinitialized are:
404: # - lonc - The lonc client process.
405: # - lond - The lond daemon.
406: # NOTE:
407: # Reinitialization in this case means re-scanning the hosts table,
408: # starting new lond/lonc's as approprate and stopping existing lonc/lond's.
409: #
410: # Parameters:
411: # process - The name of the process to reinit (lonc or lond).
412: # host - The host in which this reinit will happen.
413: #
1.13 foxr 414: # >>>BUGBUG<<<< This belongs in lonnet.pm
415: #
1.9 foxr 416: sub ReinitProcess {
417: my $process = shift;
418: my $host = shift;
1.3 foxr 419:
1.9 foxr 420: # Ensure the host is valid:
421:
422: if(!ValidHost($host)) {
423: die "EHOSTINVAL - Invalid host $host";
424: }
425: # Ensure target process selector is valid:
426:
427: if(($process eq "lonc") ||
428: ($process eq "lond")) {
1.16 foxr 429: print("Reinitializing $process in $host\n");
1.9 foxr 430: Transact($host, "reinit:$process");
431: } else {
432: die "EINVAL -Invalid parameter. Process $process must be lonc or lond";
433: }
1.7 foxr 434: }
1.6 foxr 435: #--------------------------- Entry point: --------------------------
436:
1.16 foxr 437:
438:
1.6 foxr 439: # Parse the parameters
440: # If command parsing failed, then print usage:
1.2 foxr 441:
1.7 foxr 442: my @params = ParseArgs;
443: my $nparam = @params;
1.3 foxr 444:
445: if($nparam == 0) {
1.2 foxr 446: Usage;
1.4 foxr 447: exit -1;
1.2 foxr 448: }
1.7 foxr 449: #
450: # Next, ensure we are running as EID root.
451: #
452: if ($EUID != 0) {
453: die "ENOPRIV - No privilege for requested operation"
1.6 foxr 454: }
455:
1.19 foxr 456: #
457: # Read the configuration file.
458: #
459:
460: ReadConfig; # Read the configuration info (incl.hosts).
1.4 foxr 461:
1.6 foxr 462: # Based on the operation requested invoke the appropriate function:
463:
1.7 foxr 464: my $operation = shift @params;
1.6 foxr 465:
466: if($operation eq "push") { # push tablename filename host
1.7 foxr 467: my $tablename = shift @params;
468: my $tablefile = shift @params;
469: my $host = shift @params;
1.16 foxr 470: if($host) {
471: PushFile($tablename, $tablefile, $host);
472: } else { # Push to whole cluster.
473: foreach my $host (keys %hostshash) {
474: PushFile($tablename, $tablefile, $host);
475: }
476: }
1.6 foxr 477:
1.7 foxr 478: } elsif($operation eq "reinit") { # reinit processname host.
479: my $process = shift @params;
480: my $host = shift @params;
1.16 foxr 481: if ($host) {
482: ReinitProcess($process, $host);
483: } else { # Reinit whole cluster.
484: foreach my $host (keys %hostshash) {
485: ReinitProcess($process,$host);
486: }
487: }
488: }
1.7 foxr 489: else {
490: Usage;
1.6 foxr 491: }
1.4 foxr 492: exit 0;
1.2 foxr 493:
494: =head1 NAME
495: lonManage - Command line utility for remote management of lonCAPA
496: cluster nodes.
497:
498: =head1 SYNOPSIS
499:
500: Usage:
1.3 foxr 501: B<lonManage --push=<tablename> newfile host>
1.2 foxr 502: Push <tablename> to the lonTabs directory. Note that
503: <tablename> must be one of:
504: hosts (hosts.tab)
505: domain (domain.tab)
506:
1.3 foxr 507: B<lonManage --reinit=lonc host>
1.2 foxr 508: Sends a HUP signal to the remote systems's lond.
509:
1.3 foxr 510: B<lonmanage --reinit=lond host>
1.2 foxr 511: Requests the remote system's lond perform the same action as if
512: it had received a HUP signal.
513:
514: In the above syntax, the host above is the hosts.tab name of a host,
515: not the IP address of the host.
516:
517:
518: =head1 DESCRIPTION
519:
520: =head1 PREREQUISITES
1.3 foxr 521:
1.7 foxr 522: =item strict
1.3 foxr 523: =item Getopt::Long
1.7 foxr 524: =item English
1.13 foxr 525: =item IO::Socket::UNIX
526:
527: =head1 KEY Subroutines.
1.2 foxr 528:
529: =head1 CATEGORIES
530: Command line utility
531:
532: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>