Diff for /loncom/Attic/lonManage between versions 1.9 and 1.25

version 1.9, 2003/08/18 10:25:46 version 1.25, 2003/11/04 11:52:06
Line 33 Line 33
 #   it is a command line tool.  The following command line syntax (usage)  #   it is a command line tool.  The following command line syntax (usage)
 #   is supported:  #   is supported:
 #  #
 #    lonManage  -push   <tablename>  newfile  host  #    lonManage  -push   <tablename>  newfile  [host]
 #        Push <tablename> to the lonTabs directory.  Note that  #        Push <tablename> to the lonTabs directory.  Note that
 #        <tablename> must be one of:  #        <tablename> must be one of:
 #           hosts  (hosts.tab)  #           host  (hosts.tab)
 #           domain (domain.tab)  #           domain (domain.tab)
 #  #
 #    lonManage  -reinit lonc host  #    lonManage  -reinit lonc [host]
 #           Sends a HUP signal to the remote systems's lond.  #           Sends a HUP signal to the remote systems's lond.
 #  #
 #    lonmanage  -reinit lond host  #    lonmanage  -reinit lond [host]
 #          Requests the remote system's lond perform the same action as if  #          Requests the remote system's lond perform the same action as if
 #          it had received a HUP signal.  #          it had received a HUP signal.
 #  #
 #    In the above syntax, the host above is the hosts.tab name of a host,  #    In the above syntax, the host above is the hosts.tab name of a host,
 #    not the IP address of the host.  #    not the IP address of the host
 #  #  
 #  $Log$  #   If [host] is not supplied, every host in the client's hosts.tab
 #  Revision 1.9  2003/08/18 10:25:46  foxr  #   table is iterated through and procesed..
 #  Write ReinitProcess function in terms of ValidHost and Transact.  
 #  
 #  Revision 1.8  2003/08/18 10:18:21  foxr  
 #  Completed PushFile function in terms of  
 #  - ValidHost - Determines if target host is valid.  
 #  - Transact  - Performs one of the valid transactions with the  
 #                appropriate lonc<-->lond client/server pairs.  
 #  
 #  Revision 1.7  2003/08/18 09:56:01  foxr  
 #  1. Require to be run as root.  
 #  2. Catch case where no operation switch is supplied and put out usage.  
 #  3. skeleton/comments for PushFile function.  
 #  
 #  Revision 1.6  2003/08/12 11:02:59  foxr  
 #  Implement command switch dispatching.  
 #  
 #  Revision 1.5  2003/08/12 10:55:42  foxr  
 #  Complete command line parsing (tested)  
 #  
 #  Revision 1.4  2003/08/12 10:40:44  foxr  
 #  Get switch parsing right.  
 #  
 #  Revision 1.3  2003/08/12 10:22:35  foxr  
 #  Put in parameter parsing infrastructure  
 #  
 #  Revision 1.2  2003/08/12 09:58:49  foxr  
 #  Add usage and skeleton documentation.  
 #  #
 #  #
   
   
   
   # Modules required:
   
   use lib ".";
   
 use strict; # Because it's good practice.  use strict; # Because it's good practice.
 use English; # Cause I like meaningful names.  use English; # Cause I like meaningful names.
 use Getopt::Long;  use Getopt::Long;
   use LondConnection;
   use IO::Poll qw(POLLRDNORM POLLWRNORM POLLIN POLLHUP POLLOUT);
   
   # File scoped variables:
   
   my %perlvar; # Perl variable defs from apache config.
   my %hostshash; # Host table as a host indexed hash.
   
   my $MyHost=""; # Host name to use as me.
   my $ForeignHostTab=""; # Name of foreign hosts table.
   
   my $DefaultServerPort =  5663; # Default server port if standalone.
   my $ServerPort; # Port used to connect to lond.
   
   my $TransitionTimeout = 5; # Poll timeout in seconds.
   
   
   # LondConnection::SetDebug(10);
   
   
   #
   #   prints out utility's command usage info.
   #
 sub Usage  {  sub Usage  {
     print "Usage:";      print "Usage:";
     print <<USAGE;      print <<USAGE;
     lonManage  --push=<tablename>  newfile  host   lonManage  [--myname=host --hosts=table] --push=<tablename>  newfile  [host]
         Push <tablename> to the lonTabs directory.  Note that          Push <tablename> to the lonTabs directory.  Note that
         <tablename> must be one of:          <tablename> must be one of:
            hosts  (hosts.tab)             host  (hosts.tab)
            domain (domain.tab)             domain (domain.tab)
   
     lonManage  --reinit=lonc host   lonManage [--myname=host --hosts=table] --reinit=lonc [host]
            Sends a HUP signal to the remote systems's lond.         Causes lonc in the remote system to reread hosts.tab and
          adjust the set of clients that are being maintained to match
     lonManage  --reinit=lond host         the new file.
           Requests the remote system's lond perform the same action as if         
           it had received a HUP signal.  
    lonManage [--myname=host --hosts=table] --reinit=lond [host]
          Causes lond in the remote system to reread the hosts.tab file
          and adjust the set of servers to match changes in that file.
   
     In the above syntax, the host above is the hosts.tab name of a host,      In the above syntax, the host above is the hosts.tab name of a host,
     not the IP address of the host.      not the IP address of the host.
   
       If [host] is omitted, all hosts in the hosts.tab file are iterated
       over.
   
    For all of the above syntaxes if --myname=host and --hosts=table are
    supplied (both must be present), the utility runs in standalone mode
    presenting itself to the world as 'host' and using the hosts.tab file
    specified in the --hosts switch.
 USAGE  USAGE
   
   
 }  }
   
 #  #
   #  Make a direct connection to the lond in 'host'.  The port is 
   #  gotten from the global variable:  ServerPort.
   #  Returns:
   #    The connection or undef if one could not be formed.
   #
   sub MakeLondConnection {
       my $host = shift;
   
       my $Connection = LondConnection->new($host, $ServerPort);
       return return $Connection;
   }
   #
   #   Process the connection state machine until the connection
   #   becomes idle. This is used both to negotiate the initial
   #   connection, during which the LondConnection sequences a rather 
   #   complex state machine and during the transaction itself
   #   for a simpler set of transitions.
   #   All we really need to be concerned with is whether or not
   #   we're readable or writable and the final state:
   #
   #   Parameter:
   #       connection   - Represents the LondConnection to be sequenced.
   #       timeout      - Maximum time to wait for readable/writable sockets.
   #                      in seconds. < 0 waits forever.
   #   Return:
   #       'ok'         - We got to idle ok.
   #       'error:msg'  - An error occured. msg describes the error.
   #
   sub SequenceStateMachine {
       my $connection   = shift;
       my $timeout      = shift;
   
       my $Socket       = $connection->GetSocket;
       my $returnstatus = "ok";              # optimist!!!
       my $error        = 0;              # Used to force early loop termination
                                                 # damned perl has no break!!.
       my $state        = $connection->GetState;
   
       while(($connection->GetState ne "Idle") && (!$error)) {
    #
    # Figure out what the connection wants. read/write and wait for it
    # or for the timeout.
    #
    my $wantread = $connection->WantReadable;
    my $poll     = new IO::Poll;
    $poll->mask($Socket, => $wantread ? POLLIN : POLLOUT);
    $poll->poll($timeout);
    my $done     = $poll->handles();
    if(scalar($done) == 0) {            # no handles ready... timeout!!
       $returnstatus  = "error:";
       $returnstatus .= "Timeout in state $state\n";
       $error         = 1;
    } else {
       my $status;
       $status        = $wantread ? $connection->Readable :
                            $connection->Writable;
       if($status != 0) {
    $returnstatus  =  "error:";
    $returnstatus .=  " I/O failed in state $state\n";
    $error = 1;
       }
    }
    $state = $connection->GetState;
       }
       return $returnstatus;
   }
   
   #
   #    This function runs through the section of the connection
   #   state machine that has to do with negotiating the startup 
   #   sequence with lond.  The general strategy is to loop
   #   until the connection state becomes idle or disconnected.
   #   Disconnected indicates an error or rejection of the
   #   connection at some point in the negotiation.
   #   idle indicates a connection ready for a request.
   #   The main loop consults the object to determine if it
   #   wants to be writeable or readable, waits for that
   #   condition on the socket (with timeout) and  then issues
   #   the appropriate LondConnection call. Note that
   #   LondConnection is capable of doing everything necessary
   #   to get to the initial idle state.
   # 
   #
   #  Parameters:
   #     connection - A connection that has been created with
   #                  the remote lond.  This connection should
   #                  be in the Connected state ready to send
   #                  the init sequence.
   #
   sub NegotiateStartup {
       my $connection = shift;
       my $returnstatus = "ok"; # Optimistic!!.
   
       my $state      = $connection->GetState;
       if($state ne "Connected") {
    print "Error: Initial lond connection state: $state should be Connected\n";
    return "error";
       }
   
       return SequenceStateMachine($connection, $TransitionTimeout);
   }
   #
   #   Perform a transaction with the remote lond.
   #   Paramters:
   #      connection - the connection object that represents
   #                   a LondConnection to the remote lond.
   #      command    - The request to send to the remote system.
   #   Returns:
   #       The 'reaction' of the lond to this command.
   #       However if the connection to lond is lost during the transaction
   #       or some other error occurs, the text "error:con_lost" is returned.
   #    
   sub PerformTransaction {
       my $connection  = shift;
       my $command     = shift;
       my $retval;                          # What we'll returnl.
   
      
       #  Set up the connection to do the transaction then
       #  do the I/O until idle or error.
       #
       $connection->InitiateTransaction($command);
   
       my $status = SequenceStateMachine($connection, $TransitionTimeout);
       if($status eq "ok") {
    $retval = $connection->GetReply;
       } else {
    $retval = $status;
       }
   
       return $retval;
   }
   #
   # Performs a transaction direct to a remote lond.
   #   Parameter:
   #      cmd  - The text of the request.
   #      host - The host to which the request ultimately goes.
   #   Returns:
   #      The text of the reply from the lond or con_lost if not able to contact
   #      lond/lonc etc.
   #
   sub subreply {
       my $cmd = shift;
       my $host = shift;
   
   
       my $connection  = MakeLondConnection($host);
       if ($connection eq undef) {
    return "Connect Failed";
       }
       my $reply = NegotiateStartup($connection);
       if($reply ne "ok") {
    return "connection negotiation failed";
       }
       my $reply =  PerformTransaction($connection, $cmd);
       return $reply;
   
   
        #    my ($cmd,$server)=@_;
        #    my $peerfile="$perlvar{'lonSockDir'}/$server";
        #    my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
        #                                     Type    => SOCK_STREAM,
        #                                     Timeout => 10)
        #       or return "con_lost";
        #    print $client "$cmd\n";
        #    my $answer=<$client>;
        #    if (!$answer) { $answer="con_lost"; }
        #    chomp($answer);
        #    return $answer;
   }
   #   >>> BUGBUG <<< 
   #
 #  Use Getopt::Long to parse the parameters of the program.  #  Use Getopt::Long to parse the parameters of the program.
 #  #
 #  Return value is a list consisting of:  #  Return value is a list consisting of:
Line 130  USAGE Line 315  USAGE
 #   returns an empty list if the parse fails.  #   returns an empty list if the parse fails.
 #  #
   
   
 sub ParseArgs {  sub ParseArgs {
     my $pushing   = '';      my $pushing   = '';
     my $reinitting = '';      my $reinitting = '';
   
     if(!GetOptions('push=s'    => \$pushing,      if(!GetOptions('push=s'    => \$pushing,
            'reinit=s'  => \$reinitting)) {             'reinit=s'  => \$reinitting,
      'myname=s' => \$MyHost,
      'hosts=s' => \$ForeignHostTab)) {
    return ();
       }
       #  The --myname and --hosts switch must have values and
       #  most both appear if either appears:
   
       if(($MyHost ne "") && ($ForeignHostTab eq "")) {
    return ();
       }
       if(($ForeignHostTab ne "") && ($MyHost eq "")) {
  return ();   return ();
     }      }
   
Line 148  sub ParseArgs { Line 345  sub ParseArgs {
   
     if($pushing ne '') {      if($pushing ne '') {
   
         # --push takes in addition a table, and a host:          # --push takes in addition a table, and an optional  host:
         #          #
  if($paramcount != 2) {   if(($paramcount != 2) && ($paramcount != 1)) {
     return (); # Invalid parameter count.      return (); # Invalid parameter count.
  }   }
  if($command ne '') {   if($command ne '') {
Line 164  sub ParseArgs { Line 361  sub ParseArgs {
   
     if ($reinitting ne '') {      if ($reinitting ne '') {
   
  # --reinit takes in addition just a host name   # --reinit takes in addition just an optional  host name
   
  if($paramcount != 1) {   if($paramcount > 1) {
     return ();      return ();
  }   }
  if($command ne '') {   if($command ne '') {
Line 187  sub ParseArgs { Line 384  sub ParseArgs {
           
     return @result;      return @result;
 }  }
   #
   #  Read the loncapa configuration stuff.  If ForeignHostTab is empty,
   #  assume we are part of a loncapa cluster and read the hosts.tab
   #  file from the config directory.  Otherwise, ForeignHossTab
   #  is the name of an alternate configuration file to read in 
   #  standalone mode.
   #
   sub ReadConfig {
   
       if($ForeignHostTab eq "") {
    my $perlvarref = LondConnection::read_conf('loncapa.conf');
    %perlvar       = %{$perlvarref};
    my $hoststab   = LondConnection::read_hosts(
       "$perlvar{'lonTabDir'}/hosts.tab");
    %hostshash     = %{$hoststab};
    $MyHost        = $perlvar{lonHostID}; # Set hostname from vars.
    $ServerPort    = $perlvar{londPort};
       } else {
   
    LondConnection::ReadForeignConfig($MyHost, $ForeignHostTab);
     my $hoststab = LondConnection::read_hosts($ForeignHostTab); #  we need to know too.
     %hostshash   = %{$hoststab};
     $ServerPort    = $DefaultServerPort;
         }
       
   }
   #
   #  Determine if the target host is valid.
   #  This is done by reading the current hosts.tab file.
   #  For the host to be valid, it must be inthe file.
   #
   #  Parameters:
   #     host   - Name of host to check on.
   #  Returns:
   #     true   if host is valid.
   #     false  if host is invalid.
   #
 sub ValidHost {  sub ValidHost {
     return 1;      my $host       = shift;
      
   
       return defined $hostshash{$host};
   
 }  }
   
   
   
   #
   #  Performs a transaction with lonc.
   #  By the time this is called, the transaction has already been
   #  validated by the caller.
   #
   #   Parameters:
   #
   #   host    - hosts.tab name of the host whose lonc we'll be talking to.
   #   command - The base command we'll be asking lond to execute.
   #   body    - [optional] If supplied, this is a command body that is a ref.
   #             to an array of lines that will be appended to the 
   #             command.
   #
   #  NOTE:
   #    The command will be done as an encrypted operation.
   #
 sub Transact {  sub Transact {
       my $host    = shift;
       my $command = shift;
       my $haveBody= 0;
       my $body;
       my $i;
   
       if(scalar @ARG) {
    $body = shift;
    $haveBody = 1;
       }
       #  Construct the command to send to the server:
       
       my $request = "encrypt\:"; # All requests are encrypted.
       $request   .= $command;
       if($haveBody) {
    $request .= "\:";
    my $bodylines = scalar @$body;
    for($i = 0; $i < $bodylines; $i++) {
       $request .= $$body[$i];
    }
       } else {
    $request .= "\n";
       }
       # Body is now built... transact with lond..
       
       my $answer = subreply($request, $host);
   
       print "$answer\n";
   
 }  }
 #  #
 #   Called to push a file to the remote system.  #   Called to push a file to the remote system.
Line 208  sub Transact { Line 494  sub Transact {
 #     tablefile - name of the file containing the table to push.  #     tablefile - name of the file containing the table to push.
 #     host      - name of the host to push this file to.       #     host      - name of the host to push this file to.     
 #  #
   #    >>>BUGBUG<<< This belongs in lonnet.pm.
   #
 sub PushFile {  sub PushFile {
     my $tablename = shift;      my $tablename = shift;
     my $tablefile = shift;      my $tablefile = shift;
Line 231  sub PushFile { Line 519  sub PushFile {
   
     if( ($tablename eq "host")    ||      if( ($tablename eq "host")    ||
  ($tablename eq "domain")) {   ($tablename eq "domain")) {
  Transact($host, "pushfile:$tablename:",\@table);   print("Pushing $tablename to $host\n");
    Transact($host, "pushfile:$tablename",\@table);
     } else {      } else {
  die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain";   die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain";
     }      }
Line 249  sub PushFile { Line 538  sub PushFile {
 #     process - The name of the process to reinit (lonc or lond).  #     process - The name of the process to reinit (lonc or lond).
 #     host    - The host in which this reinit will happen.  #     host    - The host in which this reinit will happen.
 #  #
   #   >>>BUGBUG<<<< This belongs  in lonnet.pm
   #
 sub ReinitProcess {  sub ReinitProcess {
     my $process = shift;      my $process = shift;
     my $host    = shift;      my $host    = shift;
Line 262  sub ReinitProcess { Line 553  sub ReinitProcess {
   
     if(($process eq "lonc") ||      if(($process eq "lonc") ||
        ($process eq "lond")) {         ($process eq "lond")) {
    print("Reinitializing $process in $host\n");
  Transact($host, "reinit:$process");   Transact($host, "reinit:$process");
     } else {      } else {
  die "EINVAL -Invalid parameter. Process $process must be lonc or lond";   die "EINVAL -Invalid parameter. Process $process must be lonc or lond";
Line 269  sub ReinitProcess { Line 561  sub ReinitProcess {
 }  }
 #--------------------------- Entry point: --------------------------  #--------------------------- Entry point: --------------------------
   
   
   
 #  Parse the parameters  #  Parse the parameters
 #  If command parsing failed, then print usage:  #  If command parsing failed, then print usage:
   
Line 286  if ($EUID != 0) { Line 580  if ($EUID != 0) {
     die "ENOPRIV - No privilege for requested operation"      die "ENOPRIV - No privilege for requested operation"
 }  }
   
   #
   #   Read the configuration file.
   #   
   
   ReadConfig; # Read the configuration info (incl.hosts).
   
 #   Based on the operation requested invoke the appropriate function:  #   Based on the operation requested invoke the appropriate function:
   
Line 295  if($operation eq "push") {  # push table Line 594  if($operation eq "push") {  # push table
     my $tablename = shift @params;      my $tablename = shift @params;
     my $tablefile = shift @params;      my $tablefile = shift @params;
     my $host      = shift @params;      my $host      = shift @params;
     PushFile($tablename, $tablefile, $host);      if($host) {
    PushFile($tablename, $tablefile, $host);
       } else { # Push to whole cluster.
    foreach my $host (keys %hostshash) {
       PushFile($tablename, $tablefile, $host);
    }
       }
   
 } elsif($operation eq "reinit") { # reinit processname host.  } elsif($operation eq "reinit") { # reinit processname host.
     my $process   = shift @params;      my $process   = shift @params;
     my $host      = shift @params;      my $host      = shift @params;
     ReinitProcess($process, $host);      if ($host) {
 }   ReinitProcess($process, $host);
       } else { # Reinit whole cluster.
    foreach my $host (keys %hostshash) {
       ReinitProcess($process,$host);
    }
       }
   } 
 else {  else {
     Usage;      Usage;
 }  }
Line 338  Usage: Line 649  Usage:
 =item strict  =item strict
 =item Getopt::Long  =item Getopt::Long
 =item English  =item English
   =item IO::Socket::UNIX
   
   =head1 KEY Subroutines.
   
 =head1  CATEGORIES  =head1  CATEGORIES
     Command line utility      Command line utility

Removed from v.1.9  
changed lines
  Added in v.1.25


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>