--- loncom/Attic/lonManage 2003/11/03 10:39:24 1.21 +++ loncom/Attic/lonManage 2003/11/04 11:52:06 1.25 @@ -3,9 +3,9 @@ # # lonManage supports remote management of nodes in a LonCAPA cluster. # -# $Id: lonManage,v 1.21 2003/11/03 10:39:24 foxr Exp $ +# $Id: lonManage,v 1.25 2003/11/04 11:52:06 foxr Exp $ # -# $Id: lonManage,v 1.21 2003/11/03 10:39:24 foxr Exp $ +# $Id: lonManage,v 1.25 2003/11/04 11:52:06 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -63,8 +63,8 @@ use lib "."; use strict; # Because it's good practice. use English; # Cause I like meaningful names. use Getopt::Long; -use IO::Socket::UNIX; # To communicate with lonc. use LondConnection; +use IO::Poll qw(POLLRDNORM POLLWRNORM POLLIN POLLHUP POLLOUT); # File scoped variables: @@ -73,8 +73,16 @@ my %hostshash; # Host table as a host 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. # @@ -112,21 +120,138 @@ 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; - return "junk"; + + 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!!. - return "ok"; + 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. - return "ok"; + + # 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. @@ -147,7 +272,7 @@ sub subreply { return "Connect Failed"; } my $reply = NegotiateStartup($connection); - if($reply != "ok") { + if($reply ne "ok") { return "connection negotiation failed"; } my $reply = PerformTransaction($connection, $cmd); @@ -277,11 +402,13 @@ sub ReadConfig { $MyHost = $perlvar{lonHostID}; # Set hostname from vars. $ServerPort = $perlvar{londPort}; } else { - my $hoststab = LondConnection::read_hosts($ForeignHostTab); - %hostshash = %{$hoststab}; - $ServerPort = 5663; - } - + + 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.