예제 #1
0
        private Int32 OpenChannel_1_0( Int32 ConnectionHandle, Hashtable InParameters, ref Hashtable OutParameters )
        {
            StartTiming( "OpenChannel_1_0-Internal", true );

            Int32 ErrorCode = Constants.INVALID;

            // First validate the connection handle
            Connection ConnectionThatOwnsChannel;
            if( Connections.TryGetValue( ConnectionHandle, out ConnectionThatOwnsChannel ) )
            {
                // Unpack the input parameters
                string ChannelName = InParameters["ChannelName"] as string;
                EChannelFlags ChannelFlags = ( EChannelFlags )InParameters["ChannelFlags"];

                Log( EVerbosityLevel.ExtraVerbose, ELogColour.Green, "[OpenChannel] Opening Channel: " + ChannelName );

                // Whether the cache request is a hit
                bool CacheIsHit = true;

                // Determine the proper path for the file and do any necessary preparing
                // depending on the type of channel that's being requested
                try
                {
                    ErrorCode = EnsureFolderExists( AgentApplication.Options.CacheFolder );
                    if( ErrorCode >= 0 )
                    {
                        if( ( ChannelFlags & EChannelFlags.TYPE_PERSISTENT ) != 0 )
                        {
                            string AgentStagingArea = Path.Combine( AgentApplication.Options.CacheFolder, "AgentStagingArea" );
                            string StagingAreaChannelName = Path.Combine( AgentStagingArea, ChannelName );
                            string FullyCachedChannelName = Path.Combine( AgentApplication.Options.CacheFolder, ChannelName );

                            // A channel is opened for writing in the staging area and moved to the cache on Close
                            if( ( ChannelFlags & EChannelFlags.ACCESS_WRITE ) != 0 )
                            {
                                // Ensure the necessary folder exists
                                ErrorCode = EnsureFolderExists( AgentStagingArea );
                                if( ErrorCode >= 0 )
                                {
                                    // Delete the file if it exists in the staging area
                                    FileInfo StagingAreaChannel = new FileInfo( StagingAreaChannelName );
                                    if( StagingAreaChannel.Exists )
                                    {
                                        StagingAreaChannel.IsReadOnly = false;
                                        StagingAreaChannel.Delete();
                                    }

                                    // Delete the file if it exists in the main cache area
                                    FileInfo FullyCachedChannel = new FileInfo( FullyCachedChannelName );
                                    if( FullyCachedChannel.Exists )
                                    {
                                        FullyCachedChannel.IsReadOnly = false;
                                        FullyCachedChannel.Delete();
                                    }

                                    // Remove the channel's entry from the hash table, if one is there
                                    if( ChannelHashValues.Remove( ChannelName ) )
                                    {
                                        Log( EVerbosityLevel.Verbose, ELogColour.Green, " ......... removed hash for " + ChannelName );
                                    }

                                    // This channel is now safe to open
                                    // Create a new channel object and add it to the set of open channels
                                    Channel NewChannel = new Channel( ChannelName, StagingAreaChannelName, ChannelFlags );
                                    ErrorCode = SetChannelHandleAndAdd( ConnectionThatOwnsChannel, NewChannel );
                                }
                            }
                            // A channel opened for reading is opened directly in the cache
                            else if( ( ChannelFlags & EChannelFlags.ACCESS_READ ) != 0 )
                            {
                                // If the file doesn't exist, see if we can recover
                                if( !File.Exists( FullyCachedChannelName ) )
                                {
                                    if( !File.Exists( StagingAreaChannelName ) )
                                    {
                                        // If the file doesn't exist, there are a couple cases we need to
                                        // check before we call this a failure. If this connection is a Job
                                        // running for a remote Agent, the channel is an implicit dependency
                                        // and needs to be requested from the remote Agent.
                                        if( ( ConnectionThatOwnsChannel.Parent != null ) &&
                                            ( ConnectionThatOwnsChannel.Parent is RemoteConnection ) )
                                        {
                                            // Request the missing channel via the remote parent connection
                                            CacheIsHit = false;
                                            RemoteConnection RemoteParentConnection = ConnectionThatOwnsChannel.Parent as RemoteConnection;
                                            if( !PullChannel( RemoteParentConnection, ChannelName, null ) )
                                            {
                                                // If the pull failed, set the correct error based on the state of the remote
                                                if( RemoteParentConnection.Interface.IsAlive() )
                                                {
                                                    ErrorCode = Constants.ERROR_CHANNEL_NOT_FOUND;
                                                }
                                                else
                                                {
                                                    ErrorCode = Constants.ERROR_CONNECTION_DISCONNECTED;
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // This suggests that the file is still being transferred
                                        // and the caller should wait and try the request again
                                        ErrorCode = Constants.ERROR_CHANNEL_NOT_READY;
                                    }
                                }
                                // If the file does exist, we still may need to validate it with the
                                // remote Instigator using the hash value we have for the channel
                                else if( ( ConnectionThatOwnsChannel.Parent != null ) &&
                                         ( ConnectionThatOwnsChannel.Parent is RemoteConnection ) )
                                {
                                    RemoteConnection RemoteParentConnection = ConnectionThatOwnsChannel.Parent as RemoteConnection;

                                    byte[] LocalHashValue;
                                    bool ChannelHashIsValid = false;
                                    if( !ChannelHashValues.TryGetValue( ChannelName, out LocalHashValue ) )
                                    {
                                        LocalHashValue = ComputeHash( File.ReadAllBytes( FullyCachedChannelName ) );
                                        if( ChannelHashValues.Add( ChannelName, LocalHashValue ) )
                                        {
                                            Log( EVerbosityLevel.Verbose, ELogColour.Green, " ......... (3) created hash for " + ChannelName );
                                        }
                                    }

                                    if( LocalHashValue.Length > 0 )
                                    {
                                        Int32 ParentConnectionHandle = RemoteParentConnection.Handle;
                                        ChannelHashIsValid = RemoteParentConnection.Interface.ValidateChannel( ParentConnectionHandle, ChannelName, LocalHashValue );
                                    }
                                    else
                                    {
                                        Log( EVerbosityLevel.Informative, ELogColour.Orange, "[OpenChannel] Error: missing hash table entry for existing channel, pulling " + ChannelName );
                                    }
                                    // If we fail the hash comparison test, we need to pull the file
                                    if( !ChannelHashIsValid )
                                    {
                                        Log( EVerbosityLevel.Informative, ELogColour.Orange, "[OpenChannel] Remote hash comparison failed, re-pulling channel " + ChannelName );
                                        CacheIsHit = false;

                                        // Before pulling the channel, delete what we've got on disk, in case the pull fails
                                        FileInfo FullyCachedChannel = new FileInfo( FullyCachedChannelName );
                                        FullyCachedChannel.IsReadOnly = false;
                                        FullyCachedChannel.Delete();

                                        // Remove the channel's entry from the hash table, if one is there
                                        if( ChannelHashValues.Remove( ChannelName ) )
                                        {
                                            Log( EVerbosityLevel.Verbose, ELogColour.Green, " ......... removed hash for " + ChannelName );
                                        }

                                        if( !PullChannel( RemoteParentConnection, ChannelName, null ) )
                                        {
                                            // If the pull failed, set the correct error based on the state of the remote
                                            if( RemoteParentConnection.Interface.IsAlive() )
                                            {
                                                ErrorCode = Constants.ERROR_CHANNEL_NOT_FOUND;
                                            }
                                            else
                                            {
                                                ErrorCode = Constants.ERROR_CONNECTION_DISCONNECTED;
                                            }
                                        }
                                    }
                                }

                                // Open the file if it exists
                                if( File.Exists( FullyCachedChannelName ) )
                                {
                                    // Ensure we have a valid hash entry for this channel
                                    if( ChannelHashValues.Add( ChannelName, ComputeHash( File.ReadAllBytes( FullyCachedChannelName ) ) ) )
                                    {
                                        // Add the entry into the channel hash table
                                        Log( EVerbosityLevel.Verbose, ELogColour.Green, " ......... (4) created hash for " + ChannelName );
                                    }
                                    else
                                    {
                                        // TODO: Add an option for fully validated cache usage and, when
                                        // set, add code here to regenerate the hash and compare it here
                                    }

                                    // This channel is now safe to open
                                    // Create a new channel object and add it to the set of open channels
                                    Channel NewChannel = new Channel( ChannelName, FullyCachedChannelName, ChannelFlags );
                                    ErrorCode = SetChannelHandleAndAdd( ConnectionThatOwnsChannel, NewChannel );
                                }
                                else
                                {
                                    // If the file does not exist, make sure we don't have a stale hash for it
                                    if( ChannelHashValues.Remove( ChannelName ) )
                                    {
                                        Log( EVerbosityLevel.Verbose, ELogColour.Green, " ......... removed hash for " + ChannelName );
                                    }
                                }
                            }
                        }
                        else if( ( ChannelFlags & EChannelFlags.TYPE_JOB_ONLY ) != 0 )
                        {
                            AgentGuid JobGuid;
                            // If there's a Job associated with this connection, use its GUID
                            if( ConnectionThatOwnsChannel.Job != null )
                            {
                                JobGuid = ConnectionThatOwnsChannel.Job.JobGuid;
                            }
                            else
                            {
                                // If there's no Job associated with this connection at this point, provide
                                // a default GUID for one for debugging access to the agent cache
                                JobGuid = DebuggingJobGuid;
                            }

                            string AllJobsFolder = Path.Combine( AgentApplication.Options.CacheFolder, "Jobs" );
                            string ThisJobFolder = Path.Combine( AllJobsFolder, "Job-" + JobGuid.ToString() );

                            // Be sure that the folders we need are there (should have all been created in OpenJob)
                            if( Directory.Exists( ThisJobFolder ) )
                            {
                                string FullJobChannelName = Path.Combine( ThisJobFolder, ChannelName );

                                // A channel opened for writing is opened directly in the Jobs folder
                                if( ( ChannelFlags & EChannelFlags.ACCESS_WRITE ) != 0 )
                                {
                                    // Delete the file if it already exists
                                    FileInfo FullChannel = new FileInfo( FullJobChannelName );
                                    if( FullChannel.Exists )
                                    {
                                        FullChannel.IsReadOnly = false;
                                        FullChannel.Delete();
                                    }

                                    // This channel is now safe to open
                                    // Create a new channel object and add it to the set of open channels
                                    Channel NewJobChannel = new JobChannel( JobGuid, ChannelName, FullJobChannelName, ChannelFlags );
                                    ErrorCode = SetChannelHandleAndAdd( ConnectionThatOwnsChannel, NewJobChannel );
                                }
                                // A channel opened for reading is opened directly from the Jobs folder
                                else if( ( ChannelFlags & EChannelFlags.ACCESS_READ ) != 0 )
                                {
                                    // If the file doesn't exist, see if we can recover
                                    if( !File.Exists( FullJobChannelName ) )
                                    {
                                        // If the file doesn't exist, there are a couple cases we need to
                                        // check before we call this a failure. If this connection is a Job
                                        // running for a remote Agent, the channel is an implicit dependency
                                        // and needs to be requested from the remote Agent.
                                        if( ( ConnectionThatOwnsChannel.Parent != null ) &&
                                            ( ConnectionThatOwnsChannel.Parent is RemoteConnection ) )
                                        {
                                            // Request the missing channel via the remote parent connection
                                            CacheIsHit = false;
                                            RemoteConnection RemoteParentConnection = ConnectionThatOwnsChannel.Parent as RemoteConnection;
                                            if( !PullChannel( RemoteParentConnection, ChannelName, JobGuid ) )
                                            {
                                                // If the pull failed, set the correct error based on the state of the remote
                                                if( RemoteParentConnection.Interface.IsAlive() )
                                                {
                                                    ErrorCode = Constants.ERROR_CHANNEL_NOT_FOUND;
                                                }
                                                else
                                                {
                                                    ErrorCode = Constants.ERROR_CONNECTION_DISCONNECTED;
                                                }
                                            }
                                        }
                                    }

                                    // Open the file if it exists
                                    if( File.Exists( FullJobChannelName ) )
                                    {
                                        // This channel is now safe to open
                                        // Create a new channel object and add it to the set of open channels
                                        Channel NewJobChannel = new JobChannel( JobGuid, ChannelName, FullJobChannelName, ChannelFlags );
                                        ErrorCode = SetChannelHandleAndAdd( ConnectionThatOwnsChannel, NewJobChannel );
                                    }
                                    else
                                    {
                                        // File doesn't exist, error
                                        ErrorCode = Constants.ERROR_CHANNEL_NOT_FOUND;
                                    }
                                }
                            }
                        }
                    }
                }
                catch( Exception Ex )
                {
                    Log( EVerbosityLevel.Informative, ELogColour.Red, "OpenChannel failed: " + Ex.ToString() );
                    ErrorCode = Constants.ERROR_EXCEPTION;
                }

                // Register the cache request stats if the channel is being opened for READ
                if( ( ChannelFlags & EChannelFlags.ACCESS_READ ) != 0 )
                {
                    ConnectionThatOwnsChannel.CacheRequests++;
                    // Only register the hit/miss data if the channel is opened
                    if( ErrorCode > 0 )
                    {
                        ConnectionThatOwnsChannel.CacheHits += CacheIsHit ? 1 : 0;
                        ConnectionThatOwnsChannel.CacheMisses += CacheIsHit ? 0 : 1;
                    }
                    if( ConnectionThatOwnsChannel.Job != null )
                    {
                        ConnectionThatOwnsChannel.Job.CacheRequests++;
                        // Only register the hit/miss data if the channel is opened
                        if( ErrorCode > 0 )
                        {
                            ConnectionThatOwnsChannel.Job.CacheHits += CacheIsHit ? 1 : 0;
                            ConnectionThatOwnsChannel.Job.CacheMisses += CacheIsHit ? 0 : 1;
                        }
                    }
                    if( ConnectionThatOwnsChannel.Parent != null )
                    {
                        ConnectionThatOwnsChannel.Parent.CacheRequests++;
                        // Only register the hit/miss data if the channel is opened
                        if( ErrorCode > 0 )
                        {
                            ConnectionThatOwnsChannel.Parent.CacheHits += CacheIsHit ? 1 : 0;
                            ConnectionThatOwnsChannel.Parent.CacheMisses += CacheIsHit ? 0 : 1;
                        }
                    }
                }
            }
            else
            {
                // Not a valid connection, return error
                ErrorCode = Constants.ERROR_CONNECTION_NOT_FOUND;
            }

            StopTiming();
            return ErrorCode;
        }
예제 #2
0
        private Int32 SetChannelHandleAndAdd( Connection ConnectionThatOwnsChannel, Channel NewChannel )
        {
            Int32 NewHandleValue;
            lock( SetChannelHandleAndAddLock )
            {
                // Generate a new random value for the handle
                do
                {
                    NewHandleValue = SetChannelHandleAndAddGenerator.Next();
                }
                // Keep generating new values until we find one not already in use
                while( ConnectionThatOwnsChannel.OpenChannels.ContainsKey( NewHandleValue ) );

                // Now set the new handle value and add to the set
                NewChannel.SetHandle( NewHandleValue );
                ConnectionThatOwnsChannel.OpenChannels.Add( NewHandleValue, NewChannel );
            }
            return NewHandleValue;
        }