/////////////////////////////////////////////////////////////////////////// /** * Standard constructor */ public RemoteConnection( Int32 ConnectionHandle, AgentInfo NewRemoteAgentInfo, RemoteConnectionInterfaceWrapper NewRemoteInterface ) : base(ConnectionHandle) { Info = NewRemoteAgentInfo; Interface = NewRemoteInterface; }
/** * Used by an agent to open a remote connection to another agent. Remote * connections are bi-directional and this routine, along with its pair, * ConfirmRemoteConnection, are used to resolve all handshaking. Remote * connections are used by Agents to work on Tasks for Jobs they're * managing on behalf of a LocalConnection. */ public Int32 OpenRemoteConnection( string RequestingAgentName, Int32 ConnectionHandle, ELogFlags LoggingFlags, bool IsAssigned, DateTime AssignedTimestamp ) { // Always wait until the agent is fully initialized Initialized.WaitOne(); Int32 RemoteConnectionHandle = Constants.INVALID; CreateTimings( LoggingFlags ); StartTiming( "OpenRemoteConn-Internal", true ); // Make sure we only have one thread in here at a time lock( Connections ) { // Reject any incoming remote connection if: // we're in standalone mode // of if the CPU is already busy // or if a restart/exit has been requested if( ( AgentApplication.Options.EnableStandaloneMode ) || ( CurrentState == AgentState.Busy ) || ( AgentIsShuttingDown ) ) { return Constants.INVALID; } else { // Determine if this is a request for a connection or an assignment if( IsAssigned ) { // Make sure the assignment isn't stale if( AssignedTimestamp <= LastAssignedTime ) { return Constants.INVALID; } if( Connections.Count != 0 ) { // If there are any active jobs running in which the owner is the instigator, // meaning that this agent is connected directly to the instigator, reject foreach( AgentJob Job in ActiveJobs.Values ) { if( Job.OwnerIsInstigator ) { return Constants.INVALID; } } // Close any existing remote connections in favor of this incoming one foreach( Connection ExistingConnection in Connections.Values ) { if( ExistingConnection is RemoteConnection ) { Log( EVerbosityLevel.Informative, ELogColour.Orange, "[Connection] Detected incoming assigned remote connection, closing existing remote connection" ); Hashtable OldInParameters = null; Hashtable OldOutParameters = null; CloseConnection( ExistingConnection.Handle, OldInParameters, ref OldOutParameters ); } } } } else { // For connection requests, any other connection at all will cause a rejection if( Connections.Count != 0 ) { return Constants.INVALID; } } } // First, ping the remote host to make sure we can talk to it, // starting with verifying the Agent with the Coordinator try { if( ( Coordinator != null ) && ( CoordinatorResponding ) && ( AgentApplication.Options.EnableStandaloneMode == false ) ) { AgentInfo RequestingAgentInfo = null; Hashtable RequestedConfiguration = new Hashtable(); RequestedConfiguration["Version"] = new Version( 0, 0, 0, 0 ); RequestedConfiguration["RequestingAgentName"] = Environment.MachineName; List<AgentInfo> PotentialRemoteAgents = Coordinator.GetAvailableAgents( RequestedConfiguration ); foreach( AgentInfo NextAgentInfo in PotentialRemoteAgents ) { if( NextAgentInfo.Name == RequestingAgentName ) { RequestingAgentInfo = NextAgentInfo; break; } } if( RequestingAgentInfo != null ) { Debug.Assert( RequestingAgentInfo.Configuration.ContainsKey( "IPAddress" ) ); string RequestingAgentIPAddress = RequestingAgentInfo.Configuration["IPAddress"].ToString(); ValidateHostName(RequestingAgentIPAddress); // Now, ping the Agent if( PingRemoteHost( RequestingAgentName, RequestingAgentIPAddress ) ) { // Get the remote agent's interface object and wrap it string RemoteAgentURL = String.Format( "tcp://{0}:{1}/SwarmAgent", RequestingAgentIPAddress, Properties.Settings.Default.AgentRemotingPort.ToString() ); Agent RequestingAgentInterface = ( Agent )Activator.GetObject( typeof( Agent ), RemoteAgentURL ); RemoteConnectionInterfaceWrapper WrappedRequestingAgentInterface = new RemoteConnectionInterfaceWrapper( ConnectionHandle, RequestingAgentName, RequestingAgentIPAddress, RequestingAgentInterface ); Log( EVerbosityLevel.Informative, ELogColour.Green, "[Connect] Remote agent connection object obtained: " + RequestingAgentName ); // Confirm the connection try { // Send the connection GUID back to the requesting agent as // confirmation that we've received the request and plan to // accept it after the handshaking is complete RemoteConnectionHandle = WrappedRequestingAgentInterface.ConfirmRemoteConnection( ConnectionHandle ); if( RemoteConnectionHandle >= 0 ) { // Create the new remote connection object RemoteConnection NewRemoteConnection = new RemoteConnection( ConnectionHandle, RequestingAgentInfo, WrappedRequestingAgentInterface ); // There are times we want to ensure no new connections are coming online // where we'll lock the Connections dictionary (see MaintainCache). Note // that we're already in the lock above... Connections.Add( ConnectionHandle, NewRemoteConnection ); NewRemoteConnection.CurrentState = ConnectionState.CONNECTED; // Update our state and ping the coordinator CurrentState = AgentState.Working; WorkingFor = RequestingAgentName; if( IsAssigned ) { // Successful assignment LastAssignedTime = AssignedTimestamp; } PingCoordinator( true ); Log( EVerbosityLevel.Informative, ELogColour.Green, "[Connect] Remote agent connection confirmed: " + RequestingAgentName ); } } catch( Exception Ex ) { Log( EVerbosityLevel.Informative, ELogColour.Red, "[Connect] OpenRemoteConnection: " + Ex.ToString() ); RemoteConnectionHandle = Constants.ERROR_EXCEPTION; } } else { // Failed to ping, simply return with an appropriate error code Log( EVerbosityLevel.Informative, ELogColour.Red, "[Connect] Failed to ping " + RequestingAgentName + " at " + RequestingAgentIPAddress + " to confirm the remote connection" ); RemoteConnectionHandle = Constants.ERROR_CONNECTION_NOT_FOUND; } } else { // Failed to find the Agent info for the requesting Agent, simply return with an appropriate error code Log( EVerbosityLevel.Informative, ELogColour.Red, "[Connect] Failed to lookup " + RequestingAgentName + " in the Coordinator" ); RemoteConnectionHandle = Constants.ERROR_CONNECTION_NOT_FOUND; } } else { // Could not contact the Coordinator, simply return with an appropriate error code Log( EVerbosityLevel.Informative, ELogColour.Red, "[Connect] Failed to contact the Coordinator" ); RemoteConnectionHandle = Constants.ERROR_CONNECTION_NOT_FOUND; } } catch( Exception Ex ) { Log( EVerbosityLevel.Verbose, ELogColour.Red, "[Connect] Remote agent connection failed: " + RequestingAgentName ); Log( EVerbosityLevel.Verbose, ELogColour.Red, "[Connect] Exception details: " + Ex.ToString() ); } if( RemoteConnectionHandle < 0 ) { // If we get here, we have failed to create the connection Log( EVerbosityLevel.Informative, ELogColour.Red, "[Connect] Remote agent connection failed: " + RequestingAgentName ); } StopTiming(); } return RemoteConnectionHandle; }
/** * Used internally to try to open connections to remote agents */ public Int32 TryOpenRemoteConnection( LocalConnection ParentConnection, AgentInfo RemoteAgentInfo, bool IsAssigned, DateTime AssignedTimestamp, out RemoteConnection OpenedRemoteConnection ) { // Initialize the return values Int32 ErrorCode = Constants.INVALID; OpenedRemoteConnection = null; string RemoteAgentName = RemoteAgentInfo.Name; string RemoteAgentIPAddress = RemoteAgentInfo.Configuration["IPAddress"].ToString(); Log( EVerbosityLevel.Verbose, ELogColour.Green, "[Connect] Trying to open a remote connection to " + RemoteAgentName + " at " + RemoteAgentIPAddress ); bool bHostNameValid = true; try { ValidateHostName(RemoteAgentIPAddress); } catch (Exception) { ErrorCode = Constants.ERROR_EXCEPTION; bHostNameValid = false; } // First, ping the remote host to make sure we can talk to it if (bHostNameValid && PingRemoteHost(RemoteAgentName, RemoteAgentIPAddress)) { // Get the new unique handle that this connection will be known by Int32 NewConnectionHandle = GetUniqueHandle(); // Get the remote Agent interface object string RemoteAgentURL = String.Format( "tcp://{0}:{1}/SwarmAgent", RemoteAgentIPAddress, Properties.Settings.Default.AgentRemotingPort.ToString() ); Agent RemoteAgentInterface = ( Agent )Activator.GetObject( typeof( Agent ), RemoteAgentURL ); RemoteConnectionInterfaceWrapper WrappedRemoteAgentInterface = new RemoteConnectionInterfaceWrapper( NewConnectionHandle, RemoteAgentName, RemoteAgentIPAddress, RemoteAgentInterface ); Log( EVerbosityLevel.Verbose, ELogColour.Green, "[Connect] Remote agent interface object obtained for " + RemoteAgentName + " at " + RemoteAgentIPAddress ); // With the interface in hand, try to open a bi-directional connection. // Create the new RemoteConnection object and add it to the set // of pending connections, waiting for the incoming confirmation // which will move it to the active connection set. See // ConfirmRemoteConnection for where this happens. RemoteConnection NewRemoteConnection = new RemoteConnection( NewConnectionHandle, RemoteAgentInfo, WrappedRemoteAgentInterface ); PendingConnections.Add( NewConnectionHandle, NewRemoteConnection ); // Try to open the connection try { // Send the connection handle and the machine name that will // be used to construct the return URL for bi-directional // connection if( WrappedRemoteAgentInterface.OpenRemoteConnection( Environment.MachineName, NewConnectionHandle, LoggingFlags, IsAssigned, AssignedTimestamp ) >= 0 ) { // Successful confirmation, double check that the connection has moved if( PendingConnections.ContainsKey( NewConnectionHandle ) == false ) { if( Connections.ContainsKey( NewConnectionHandle ) ) { // Connection was successfully moved to the active connection set // thus the bi-directional communication link is established Log( EVerbosityLevel.Informative, ELogColour.Green, "[Connect] Successfully opened a remote connection with " + RemoteAgentName ); // Establish the parent-child relationship NewRemoteConnection.Parent = ParentConnection; NewRemoteConnection.Job = ParentConnection.Job; ParentConnection.RemoteChildren.Add( NewConnectionHandle, NewRemoteConnection ); ParentConnection.RemoteChildrenSeen++; ParentConnection.ChildrenSeen++; // Update the visualizer indicating that another remote connection is online if( NewRemoteConnection.Job.OwnerIsInstigator ) { AgentApplication.UpdateMachineState( RemoteAgentName, -1, EProgressionState.RemoteConnected ); } // Return the new handle for this connection OpenedRemoteConnection = NewRemoteConnection; ErrorCode = Constants.SUCCESS; } else { // Woah. Bad news. } } } else { // The connection failed to open, but didn't timeout and die, so it's likely just busy. // Return with a success code, but with no remote agent, signaling that it's possible // to try this one again later. ErrorCode = Constants.SUCCESS; } } catch( Exception Ex ) { Log( EVerbosityLevel.Verbose, ELogColour.Red, "[Connect] Exception: " + RemoteAgentName + ": " + Ex.ToString() ); ErrorCode = Constants.ERROR_EXCEPTION; } if( ErrorCode < 0 ) { Log( EVerbosityLevel.Verbose, ELogColour.Red, "[Connect] Failed to open a remote connection with " + RemoteAgentName ); } // Regardless of how we got here, make sure the connection has been removed // from the pending set (this is a safe call to make anytime) PendingConnections.Remove( NewConnectionHandle ); } else { // Failed to ping, simply return with an appropriate error code ErrorCode = Constants.ERROR_CONNECTION_NOT_FOUND; } return ErrorCode; }