Beispiel #1
0
        /*
         * Registers an agent with the coordinator, or updates the ping time
         * 
         * Returns true if a restart is required
         */
		public static PingResponse Ping( AgentInfo UpdatedInfo )
        {
            AgentsDirty = true;

			// Add a known set of configuration items
            UpdatedInfo.Configuration["LastPingTime"] = DateTime.UtcNow;

			// Retreive information the agent can provide for us and supply defaults
			if( !UpdatedInfo.Configuration.ContainsKey( "GroupName" ) )
			{
				UpdatedInfo.Configuration["GroupName"] = "Default";
			}
			// If the Agent is working for someone, replace the local user name with
			// the user name on the machine that it's working for
			if( UpdatedInfo.Configuration.ContainsKey( "WorkingFor" ) )
			{
				// Look up the other user name
				string BossAgentName = UpdatedInfo.Configuration["WorkingFor"] as string;
				if( ( Agents.ContainsKey( BossAgentName ) ) &&
					( Agents[BossAgentName].Configuration.ContainsKey( "UserName" ) ) )
				{
					UpdatedInfo.Configuration["UserName"] = Agents[BossAgentName].Configuration["UserName"];
				}
			}
			else
			{
				UpdatedInfo.Configuration["WorkingFor"] = "";
			}
			if( !UpdatedInfo.Configuration.ContainsKey( "AssignedTo" ) )
			{
				UpdatedInfo.Configuration["AssignedTo"] = "";
                UpdatedInfo.Configuration["AssignedTime"] = DateTime.UtcNow;
			}
			if( !UpdatedInfo.Configuration.ContainsKey( "UserName" ) )
			{
				UpdatedInfo.Configuration["UserName"] = "******";
			}

			// If the agent is remote, ping the machine to make sure we can talk to it
			// but only if the Agent didn't provide an IP address for us
			if( !UpdatedInfo.Configuration.ContainsKey( "IPAddress" ) )
			{
				// By default, we'll set the IP address to the loopback value
				UpdatedInfo.Configuration["IPAddress"] = IPAddress.Loopback;
				bool IPAddressUpdateSuccess = false;

				if( UpdatedInfo.Name != Environment.MachineName )
				{
					try
					{
						// Use the name, which can fail if DNS isn't enabled or if there's no host file
						Ping PingSender = new Ping();
						PingReply Reply = PingSender.Send( UpdatedInfo.Name );
						if( Reply.Status == IPStatus.Success )
						{
							// With a success, update the known IP address for this agent
							UpdatedInfo.Configuration["IPAddress"] = Reply.Address;
							IPAddressUpdateSuccess = true;
						}
					}
					catch( Exception )
					{
						// Any exception is a total failure
					}
				}

				// With a failure, update the agent's state to prevent usage
				if( !IPAddressUpdateSuccess )
				{
					UpdatedInfo.State = AgentState.Blocked;
				}
			}

			PingResponse Response;
			lock( Agents )
			{
				if( !Agents.ContainsKey( UpdatedInfo.Name ) )
				{
					// If this is a new agent, tell it to restart itself to pick up the latest version
					Response = PingResponse.Restart;
				}
				else if( Agents[UpdatedInfo.Name].State == AgentState.Restarting )
				{
					Response = PingResponse.Restart;

					// If this agent has been instructed to restart, keep overriding the
					// state until it's freed up to actually restart
					if( UpdatedInfo.State == AgentState.Available )
					{
						UpdatedInfo.State = AgentState.Restarted;
					}
					else
					{
						// Keep us in this cycle on the next ping
						UpdatedInfo.State = AgentState.Restarting;
					}
				}
				else
				{
					Response = PingResponse.Success;

					AgentInfo PreviousInfo = Agents[UpdatedInfo.Name];

					// If the WorkingFor changed, update the time
					string PreviousWorkingFor = PreviousInfo.Configuration["WorkingFor"] as string;
					string UpdatedWorkingFor = UpdatedInfo.Configuration["WorkingFor"] as string;
					
					DateTime UpdatedWorkingTime = DateTime.MaxValue;
					if( UpdatedWorkingFor != PreviousWorkingFor )
					{
                        UpdatedWorkingTime = DateTime.UtcNow;
					}
					else if( PreviousInfo.Configuration.ContainsKey( "WorkingTime" ) )
					{
						UpdatedWorkingTime = ( DateTime )PreviousInfo.Configuration["WorkingTime"];
					}
					UpdatedInfo.Configuration["WorkingTime"] = UpdatedWorkingTime;

					// Based on whether or not the agent is Working currently, update some state
					if( UpdatedInfo.State != AgentState.Working )
					{
						// No other agent should be AssignedTo it
						foreach( AgentInfo Agent in Agents.Values )
						{
							if( ( Agent.Configuration["AssignedTo"] as string ) == UpdatedInfo.Name )
							{
								Agent.Configuration["AssignedTo"] = "";
								Agent.Configuration["AssignedTime"] = DateTime.UtcNow;
							}
						}

						// If the previous state was Working for the same agent it's AssignedTo,
						// then we can clear the AssignedTo field
						if( PreviousInfo.State == AgentState.Working )
						{
							string PreviousAssignedTo = PreviousInfo.Configuration["AssignedTo"] as string;
							if( PreviousAssignedTo == PreviousWorkingFor )
							{
								PreviousInfo.Configuration["AssignedTo"] = "";
								PreviousInfo.Configuration["AssignedTime"] = DateTime.UtcNow;
							}
						}
					}
					else
					{
						// If it's WorkingFor someone, but not AssignedTo anyone else, update the AssignedTo
						if( ( ( UpdatedInfo.Configuration["WorkingFor"] as string ) != "" ) &&
							( ( PreviousInfo.Configuration["AssignedTo"] as string ) == "" ) )
						{
							PreviousInfo.Configuration["AssignedTo"] = UpdatedInfo.Configuration["WorkingFor"];
							PreviousInfo.Configuration["AssignedTime"] = DateTime.UtcNow;
						}
					}

					string UpdatedAssignedTo = PreviousInfo.Configuration["AssignedTo"] as string;
					UpdatedInfo.Configuration["AssignedTo"] = UpdatedAssignedTo;
					UpdatedInfo.Configuration["AssignedTime"] = DateTime.UtcNow;
				}

				// Set or reset the agent info to the latest
				Agents[UpdatedInfo.Name] = UpdatedInfo;
			}
			return Response;
        }
Beispiel #2
0
 public PingResponse Ping( AgentInfo Agent )
 {
     return ( Coordinator.Ping( Agent ) );
 }
Beispiel #3
0
		// Available < Busy < Other (Working)
		private static int AliveAgentSorter( AgentInfo A, AgentInfo B )
		{
			if( A.State == AgentState.Available )
			{
				if( B.State == AgentState.Available )
				{
					return 0;
				}
				else
				{
					return -1;
				}
			}
			else
			{
				if( B.State == AgentState.Available )
				{
					return 1;
				}
				else
				{
					// Neither are Available, sort on Busy
					if( A.State == AgentState.Busy )
					{
						if( B.State == AgentState.Busy )
						{
							return 0;
						}
						else
						{
							return -1;
						}
					}
					else
					{
						if( B.State == AgentState.Busy )
						{
							return 1;
						}
						else
						{
							return 0;
						}
					}

				}
			}
		}
Beispiel #4
0
		private bool ValidateDeterministicDistributionRequirements()
		{
			// If there's no record of the last run, then fail immediately
			if( Manager.LastSuccessfulJobRecord == null )
			{
				return false;
			}

			AgentJobRecord LastSuccessfulJobRecord = Manager.LastSuccessfulJobRecord;
			bool DeterministicModeValidated = false;

			// First, compare the job specification
			bool ExecutableNameMatches = false;
			string ThisOriginalExecutableName;
			if( Specification.DependenciesOriginalNames.TryGetValue( Specification.ExecutableName, out ThisOriginalExecutableName ) )
			{
				string LastOriginalExecutableName;
				if( LastSuccessfulJobRecord.Specification.DependenciesOriginalNames.TryGetValue( LastSuccessfulJobRecord.Specification.ExecutableName, out LastOriginalExecutableName ) )
				{
					if( ThisOriginalExecutableName == LastOriginalExecutableName )
					{
						ExecutableNameMatches = true;
					}
					else
					{
						Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[Deterministic] Failed to validate Job, executable name is different" );
					}
				}
			}
			// If the executable name matches, check the set of tasks
			if( ExecutableNameMatches )
			{
				bool AllTasksMatch = false;

				// Next, make sure that all the tasks in the current specification are in the last job
				List<AgentTask> ListOfTasks = new List<AgentTask>( PendingTasks.ToArray() );
				if( ListOfTasks.Count == LastSuccessfulJobRecord.AllTasks.Count )
				{
					AgentTask NextTask;
					Int32 MatchedTaskCount = 0;
					foreach( AgentTask Task in ListOfTasks )
					{
						if( LastSuccessfulJobRecord.AllTasks.TryGetValue( Task.Specification.TaskGuid, out NextTask ) )
						{
							// For now, just compare the parameters and the cost
							if( ( NextTask.Specification.Parameters == Task.Specification.Parameters ) &&
								( NextTask.Specification.Cost == Task.Specification.Cost ) )
							{
								MatchedTaskCount++;
							}
							else
							{
								string OldTaskDetails = String.Format( "[Deterministic]     Old Task: {0}, {1}, {2}",
									NextTask.Specification.TaskGuid,
									NextTask.Specification.Parameters,
									NextTask.Specification.Cost );
								string NewTaskDetails = String.Format( "[Deterministic]     New Task: {0}, {1}, {2}",
									Task.Specification.TaskGuid,
									Task.Specification.Parameters,
									Task.Specification.Cost );
								Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[Deterministic] Failed to validate Job, task is different" );
								Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, OldTaskDetails );
								Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, NewTaskDetails );
							}
						}
					}
					if( MatchedTaskCount == LastSuccessfulJobRecord.AllTasks.Count )
					{
						// Found all current tasks in the last job record
						AllTasksMatch = true;

						// Now verify and initialize the running work queues
						LastSuccessfulJobRecord.AgentToTaskQueueMapping.Clear();
						foreach( string WorkerName in LastSuccessfulJobRecord.WorkerAgentNames )
						{
							Queue<AgentTask> TaskQueue;
							if( LastSuccessfulJobRecord.AgentToGoldenTaskQueueMapping.TryGetValue( WorkerName, out TaskQueue ) )
							{
								LastSuccessfulJobRecord.AgentToTaskQueueMapping.Add( WorkerName, new Queue<AgentTask>( TaskQueue ) );
							}
							else
							{
								// If we fail to set the enumerators, fail to validate
								AllTasksMatch = false;
							}
						}
					}
				}
				else
				{
					Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[Deterministic] Failed to validate Job, overall task count is different" );
				}
				// If all tasks are the same (enough), make sure we can acquire the same set of agents for the job
				if( AllTasksMatch )
				{
					// If all tasks match, try to connection to each of the
					// remote agents from the previous job
					Int32 WorkerAgentsEmployed = 0;
					for( Int32 i = 0; i < LastSuccessfulJobRecord.WorkerAgentNames.Count; i++ )
					{
						string WorkerAgentName = LastSuccessfulJobRecord.WorkerAgentNames[i];
						string WorkerAgentIPAddress = LastSuccessfulJobRecord.WorkerAgentIPAddresses[i];

						// Don't try to connect to self and only if this name is allowed
						if( ( WorkerAgentName != Environment.MachineName ) &&
							( Manager.AgentNamePassesAllowedAgentsFilter( WorkerAgentName ) ) )
						{
							// Make sure we can open the connection and get a valid remote connection back
							RemoteConnection Remote;
							AgentInfo NewWorkerInfo = new AgentInfo();
							NewWorkerInfo.Name = WorkerAgentName;
							NewWorkerInfo.Configuration["IPAddress"] = WorkerAgentIPAddress;
							if( ( Manager.TryOpenRemoteConnection( Owner as LocalConnection, NewWorkerInfo, false, DateTime.MinValue, out Remote ) == Constants.SUCCESS ) &&
								( Remote != null ) )
							{
								WorkerAgentsEmployed++;
							}
							else
							{
								Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[Deterministic] Could not acquire connection to " + WorkerAgentName + " necessary for job" );
							}
						}
						else
						{
							// We always count ourselves
							WorkerAgentsEmployed++;
						}
					}
					if( WorkerAgentsEmployed == LastSuccessfulJobRecord.WorkerAgentNames.Count )
					{
						// If all necessary agents are employed, we've verified everything
						// we can -> success
						Manager.Log( EVerbosityLevel.Informative, ELogColour.Green, "[Deterministic] Job validated, deterministic distribution enabled" );
						DeterministicModeValidated = true;
					}
					else
					{
						Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[Deterministic] Failed to validate Job, could not acquire all agents necessary" );
					}
				}
			}
			return DeterministicModeValidated;
		}
Beispiel #5
0
        /**
         * Maintain a connection to the coordinator
         */
        public bool PingCoordinator( bool ForcePing )
        {
            StartTiming( "PingCoordinator-Internal", true );

            bool CoordinatorPinged = false;

            // Determine if we should ping
            #if !__MonoCS__ // @todo Mac
            bool ShouldPing =
                ( ForcePing ) ||
                (DateTime.UtcNow > LastCoordinatorPingTime + TimeSpan.FromMinutes(1));
            #else
            bool ShouldPing = false;
            #endif

            if( ShouldPing )
            {
                // Determine if we can ping
                bool CouldPing =
                    ( Coordinator != null ) &&
                    ( CoordinatorResponding ) &&
                    ( PingRemoteHost( AgentApplication.Options.CoordinatorRemotingHost, AgentApplication.Options.CoordinatorRemotingHost ) );

                if( CouldPing )
                {
                    LastCoordinatorPingTime = DateTime.UtcNow;

                    // Try to ping the coordinator
                    try
                    {
                        // The AgentInfo defaults cover almost everything, fill in the state
                        AgentInfo AgentInfoUpdate = new AgentInfo();
                        AgentInfoUpdate.Version = CurrentVersion;

                        // We'll only advertise the number of cores we'll have available for remote connections
                        AgentInfoUpdate.Configuration["LocalAvailableCores"] = AgentApplication.DeveloperOptions.LocalJobsDefaultProcessorCount;
                        AgentInfoUpdate.Configuration["RemoteAvailableCores"] = AgentApplication.DeveloperOptions.RemoteJobsDefaultProcessorCount;

                        // Update with our build group, etc.
                        AgentInfoUpdate.Configuration["UserName"] = Environment.UserName.ToUpperInvariant();
                        AgentInfoUpdate.Configuration["GroupName"] = AgentApplication.Options.AgentGroupName;
                        AgentInfoUpdate.Configuration["WorkingFor"] = WorkingFor;

            #if !__MonoCS__ // @todo Mac
                        IPAddress[] CoordinatorAddresses = Dns.GetHostAddresses(AgentApplication.Options.CoordinatorRemotingHost);
                        IPAddress[] LocalAddresses = Dns.GetHostAddresses(Dns.GetHostName());

                        if(CoordinatorAddresses.Any(CoordinatorAddress => IPAddress.IsLoopback(CoordinatorAddress) || LocalAddresses.Contains(CoordinatorAddress)))
                        {
                            AgentInfoUpdate.Configuration["IPAddress"] = IPAddress.Loopback;
                        }
                        else
                        {
                            var NetworkInterface = NetworkUtils.GetBestInterface(
                                CoordinatorAddresses.Where(CoordinatorAddress => CoordinatorAddress.AddressFamily == AddressFamily.InterNetwork).First()
                            );

                            AgentInfoUpdate.Configuration["IPAddress"] = NetworkUtils.GetInterfaceIPv4Address(NetworkInterface);
                        }
            #else
                        AgentInfoUpdate.Configuration["IPAddress"] = "192.168.0.203";//.2.9";
            #endif

                        // Check if standalone mode is enabled and use that unless we're closing down
                        if( ( CurrentState != AgentState.Closed ) &&
                            ( AgentApplication.Options.EnableStandaloneMode ) )
                        {
                            AgentInfoUpdate.State = AgentState.Standalone;
                        }
                        else
                        {
                            // Otherwise, pass through the real state
                            AgentInfoUpdate.State = CurrentState;
                        }

                        // A positive return value from the Ping is a request for restart
                        PingResponse Response = Coordinator.Ping( AgentInfoUpdate );
                        if( Response == PingResponse.Restart )
                        {
                            Log( EVerbosityLevel.Informative, ELogColour.Green, "[PingCoordinator] Restart has been requested" );

            #if !__MonoCS__
                            // Only restart if we're running the published (and restartable) version
                            if( ApplicationDeployment.IsNetworkDeployed )
                            {
                                // Only update the value if we're not already restarting
                                if( !AgentIsRestarting )
                                {
                                    // If we've got a signal to restart, request that the agent shutdown
                                    RequestRestart();
                                }
                            }
                            else
                            {
                                Log( EVerbosityLevel.Informative, ELogColour.Green, "[PingCoordinator] Restart request ignored for non-published agents" );
                            }
            #endif
                        }
                        CoordinatorPinged = true;
                    }
                    catch( Exception Ex )
                    {
                        // If we fail for any reason, just log it and try again later
                        Log( EVerbosityLevel.Informative, ELogColour.Orange, "[Ping] Communication with the coordinator failed, job distribution will be disabled until the connection is established" );
                        Log( EVerbosityLevel.ExtraVerbose, ELogColour.Orange, "Exception details: " + Ex.ToString() );

                        // Until all open connections are closed, the coordinator is abandoned
                        CoordinatorResponding = false;
                    }
                }
                else
                {
                    AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Determined that we couldn't ping the coordinator" );
                    if( Coordinator == null )
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Coordinator is null" );
                    }
                    else if( CoordinatorResponding == false )
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] CoordinatorResponding is false" );
                    }
                    else
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Coordinator ping failed" );
                    }
                }
            }
            else
            {
                AgentApplication.Log( EVerbosityLevel.SuperVerbose, ELogColour.Green, "[PingCoordinator] Determined that we shouldn't ping the coordinator right now" );
            }

            StopTiming();
            return CoordinatorPinged;
        }
Beispiel #6
0
        ///////////////////////////////////////////////////////////////////////////
        /**
         * Standard constructor
         */
        public RemoteConnection( Int32 ConnectionHandle,
								 AgentInfo NewRemoteAgentInfo,
								 RemoteConnectionInterfaceWrapper NewRemoteInterface )
            : base(ConnectionHandle)
        {
            Info = NewRemoteAgentInfo;
            Interface = NewRemoteInterface;
        }
Beispiel #7
0
        /**
         * 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;
        }
        /**
         * Maintain a connection to the coordinator
         */
        public bool PingCoordinator( bool ForcePing )
        {
            StartTiming( "PingCoordinator-Internal", true );

            bool CoordinatorPinged = false;

            // Determine if we should ping
            #if !__MonoCS__ // @todo Mac
            bool ShouldPing =
                ( ForcePing ) ||
                (DateTime.UtcNow > LastCoordinatorPingTime + TimeSpan.FromMinutes(1));
            #else
            bool ShouldPing = false;
            #endif

            if( ShouldPing )
            {
                // Determine if we can ping
                bool CouldPing =
                    ( Coordinator != null ) &&
                    ( CoordinatorResponding ) &&
                    ( PingRemoteHost( AgentApplication.Options.CoordinatorRemotingHost, AgentApplication.Options.CoordinatorRemotingHost ) );

                if( CouldPing )
                {
                    LastCoordinatorPingTime = DateTime.UtcNow;

                    // Try to ping the coordinator
                    try
                    {
                        // The AgentInfo defaults cover almost everything, fill in the state
                        AgentInfo AgentInfoUpdate = new AgentInfo();
                        AgentInfoUpdate.Version = CurrentVersion;

                        // We'll only advertise the number of cores we'll have available for remote connections
                        AgentInfoUpdate.Configuration["LocalAvailableCores"] = AgentApplication.DeveloperOptions.LocalJobsDefaultProcessorCount;
                        AgentInfoUpdate.Configuration["RemoteAvailableCores"] = AgentApplication.DeveloperOptions.RemoteJobsDefaultProcessorCount;

                        // Update with our build group, etc.
                        AgentInfoUpdate.Configuration["UserName"] = Environment.UserName.ToUpperInvariant();
                        AgentInfoUpdate.Configuration["GroupName"] = AgentApplication.Options.AgentGroupName;
                        AgentInfoUpdate.Configuration["WorkingFor"] = WorkingFor;

            #if !__MonoCS__ // @todo Mac
                        // Send our IP address for others to use
                        ManagementObjectSearcher ObjectSearcher = new ManagementObjectSearcher( "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = 'TRUE'" );
                        ManagementObjectCollection ObjectCollection = ObjectSearcher.Get();
                        bool bFoundAddress = false;
                        bool bFoundAddressWithDNS = false;

                        foreach( ManagementObject NextObject in ObjectCollection )
                        {
                            // Get the first IP addresses of the first IPEnabled network adapter
                            string[] AddressList = ( string[] )NextObject["IPAddress"];
                            string DNSDomain = (string)NextObject["DNSDomain"];

                            // Use the first address in the enumerator, or the first address with a DNS gateway if one exists
                            // Checking the DNS gateway allows adapters connected to networks to win over adapters earlier in the enumeration that are on a private network, eg connected to console
                            if (!bFoundAddress || !bFoundAddressWithDNS)
                            {
                                AgentInfoUpdate.Configuration["IPAddress"] = IPAddress.Parse(AddressList[0]);

                                bFoundAddress = true;

                                if (DNSDomain != null && DNSDomain.Length > 0)
                                {
                                    bFoundAddressWithDNS = true;
                                }
                            }
                        }
            #else
                        AgentInfoUpdate.Configuration["IPAddress"] = "192.168.0.203";//.2.9";
            #endif

                        // Check if standalone mode is enabled and use that unless we're closing down
                        if( ( CurrentState != AgentState.Closed ) &&
                            ( AgentApplication.Options.EnableStandaloneMode ) )
                        {
                            AgentInfoUpdate.State = AgentState.Standalone;
                        }
                        else
                        {
                            // Otherwise, pass through the real state
                            AgentInfoUpdate.State = CurrentState;
                        }

                        // A positive return value from the Ping is a request for restart
                        PingResponse Response = Coordinator.Ping( AgentInfoUpdate );
                        if( Response == PingResponse.Restart )
                        {
                            Log( EVerbosityLevel.Informative, ELogColour.Green, "[PingCoordinator] Restart has been requested" );

            #if !__MonoCS__
                            // Only restart if we're running the published (and restartable) version
                            if( ApplicationDeployment.IsNetworkDeployed )
                            {
                                // Only update the value if we're not already restarting
                                if( !AgentIsRestarting )
                                {
                                    // If we've got a signal to restart, request that the agent shutdown
                                    RequestRestart();
                                }
                            }
                            else
                            {
                                Log( EVerbosityLevel.Informative, ELogColour.Green, "[PingCoordinator] Restart request ignored for non-published agents" );
                            }
            #endif
                        }
                        CoordinatorPinged = true;
                    }
                    catch( Exception Ex )
                    {
                        // If we fail for any reason, just log it and try again later
                        Log( EVerbosityLevel.Informative, ELogColour.Orange, "[Ping] Communication with the coordinator failed, job distribution will be disabled until the connection is established" );
                        Log( EVerbosityLevel.ExtraVerbose, ELogColour.Orange, "Exception details: " + Ex.ToString() );

                        // Until all open connections are closed, the coordinator is abandoned
                        CoordinatorResponding = false;
                    }
                }
                else
                {
                    AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Determined that we couldn't ping the coordinator" );
                    if( Coordinator == null )
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Coordinator is null" );
                    }
                    else if( CoordinatorResponding == false )
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] CoordinatorResponding is false" );
                    }
                    else
                    {
                        AgentApplication.Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[PingCoordinator] Coordinator ping failed" );
                    }
                }
            }
            else
            {
                AgentApplication.Log( EVerbosityLevel.SuperVerbose, ELogColour.Green, "[PingCoordinator] Determined that we shouldn't ping the coordinator right now" );
            }

            StopTiming();
            return CoordinatorPinged;
        }