Viewing all Queued Builds in TFS: Part 2

| Comments

As I discussed in part one of this article, TFS 2010 does not give a friendly view for an administrator to see what all builds are currently running. Because our TFS has so many team projects, it becomes extremely annoying to dig through each project to find out when a build has hung up a build agent.

Recently, we decided to retire our existing build machines and replace them with newer, more powerful machines, machines that would be capable of running more than one build at a time. Previously, we had several build machines (5-6), each of which had a single build controller and build agent running on them. Unfortunately, if six different developers on different development teams working in different Team Projects queued up a build on the same build machine, then five of the builds would be queued on that single machine, and none of those developers (or more importantly, me!) had an easy view into what they were waiting for. Ultimately, that one machine became wasteful bottleneck, as there would be several other unused machines.

Because of this, and other reasons, we revised our build machines’ topology. Now we are using a single build controller, and then we configured twelve new build agents spread across several machines. This allowed the build controller to make use of all the available build agents and solved the bottleneck caused by our prior topology.

Unfortunately, this made the console application that I wrote obsolete. Before, the console app only reported the build controllers that had builds running on them. This was fine, as long as there was a 1:1 relationship between build machines and build controllers. But now we only have one build controller, so if there was an issue with one of the twelve build agents assigned to that controller, the console app would not be useful to us as a troubleshooting tool.

To solve this problem, I further mangled/modified the console app posted on Bart Wullem’s blog to display the build controller’s name and the build agent’s name for all queued builds.

It is pretty simple to create a new console application in Visual Studio using the source I documented below. However, if you are anything like me you sure appreciate when someone shares a binary that they can use. You can download a copy here.

Installation Directions

  1. Unzip the contents to a folder. (Optional: For ease of use, add the folder you unzipped it to into your Windows PATH environment variable)
  2. Open the folder and edit the config file (TFSBuildQueue.exe.config), there is a single configuration entry in there for the URL of your TFS Project Collection (usually: http://yourtfsserver:8080/tfs/defaultcollection)
  3. Open a command console and set the width to at least 180 columns
  4. Change Directory to the path you unzipped TFSBuildQueue.zip to.
  5. Execute tfsbuildqueue.exe

Source Code

TFSBuildQueue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Client;

namespace TFSBuildQueue
{
    class Program
    {
        static void Main(string[] args)
        {
            int BuildCount = 0;
            string TFS_URL = ConfigurationManager.AppSettings["TFS_URL"];
            Console.WriteLine("\nTFS Build Queue");
            Console.WriteLine("===============\n");
            Console.WriteLine("Connecting to: " + TFS_URL + " and querying build controllers...");
            TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new            Uri(TFS_URL));
            IBuildServer bs = tfs.GetService<IBuildServer>();
            IQueuedBuildSpec qbSpec = bs.CreateBuildQueueSpec("*", "*");
            IQueuedBuildQueryResult qbResults = bs.QueryQueuedBuilds(qbSpec);


            // Define DataTable for storage and manipulation of currently queued builds.
            DataTable QBTable = new DataTable();
            QBTable.Columns.Add("BuildMachine");
            QBTable.Columns.Add("Project");
            QBTable.Columns.Add("BuildDefinition");
            QBTable.Columns.Add("BuildStatus");
            QBTable.Columns.Add("Priority");
            QBTable.Columns.Add("Date");
            QBTable.Columns.Add("ElapsedTime");
            QBTable.Columns.Add("User");


            // Query TFS For Queued builds and write each build to QBTable
            foreach (IQueuedBuild qb in qbResults.QueuedBuilds)
            {
                qb.Build.RefreshAllDetails();
                string RequestedBy = qb.RequestedBy.PadRight(18);
                if (qb.RequestedBy != qb.RequestedFor)
                {
                    RequestedBy = String.Concat(qb.RequestedBy, " (for ", qb.RequestedFor, ")").PadRight(18);
                }
                DateTime CurrentTime = DateTime.Now;
                TimeSpan ElapsedTime = CurrentTime.Subtract(qb.QueueTime);
                string ElapsedTimeString = ElapsedTime.ToString();
                String TFSET = ElapsedTimeString;
                String TFS_TEAMPROJECT;
                String BuildAgentStr;
                String BuildMachineStr;
                if (qb.Status.ToString() != "InProgress")
                {
                    TFS_TEAMPROJECT = "-------";
                    BuildAgentStr = "N/A";
                }
                else
                {
                    TFS_TEAMPROJECT = qb.Build.TeamProject;
                    BuildAgentStr = GetBuildAgent(qb.Build);
                }
                BuildMachineStr = qb.BuildController.Name + " (" + BuildAgentStr.ToUpper() + ")";

                QBTable.Rows.Add(
                BuildMachineStr.PadRight(46),
                TFS_TEAMPROJECT.PadRight(17),
                qb.BuildDefinition.Name.PadRight(28),
                qb.Status.ToString().PadRight(11),
                qb.Priority.ToString().PadRight(9),
                qb.QueueTime.ToString().PadRight(23),
                TFSET.PadRight(17),
                RequestedBy.PadRight(19)
                );
             BuildCount++;
            }

            // Sorts QBTable on Build controller then by date
            DataRow[] QBSorted = QBTable.Select("", "BuildMachine ASC, Date ASC");

            // Writes the headers 
            WriteHeaders();

            foreach (DataRow dataRow in QBSorted)
            {
                WriteReportLine(
                    dataRow[0].ToString(),
                    dataRow[1].ToString(),
                    dataRow[2].ToString(),
                    dataRow[3].ToString(),
                    dataRow[4].ToString(),
                    dataRow[5].ToString(),
                    dataRow[6].ToString(),
                    dataRow[7].ToString());
            }


            Console.WriteLine("\n\nTotal Builds Queued: " + BuildCount + "\n\n");
        }

        static void WriteHeaders()
        {
            Console.WriteLine("\n\n");
            Console.WriteLine("Build Controller (Agent)".PadRight(46) + " " +
                              "Project".PadRight(17) + " " +
                              "Build Definition".PadRight(28) + " " +
                              "Status".PadRight(11) + " " +
                              "Priority".PadRight(9) + " " +
                              "Date & Time Started".PadRight(23) + " " +
                              "Elapsed Time".PadRight(17) + " " +
                              "User".PadRight(19));
            Console.WriteLine("============================================".PadRight(46) + " " +
                              "=======".PadRight(17) + " " +
                              "================".PadRight(28) + " " +
                              "=======".PadRight(11) + " " +
                              "========".PadRight(9) + " " +
                              "=====================".PadRight(23) + " " +
                              "================".PadRight(17) + " " +
                              "============".PadRight(19));
        }

        static void WriteReportLine(string TFSBuildController, string TFSProject, string TFSBuildDefinition, string TFSBuildStatus, string TFSBuildPriority, string TFSBuildDateTime, string ElapsedTime, string TFSBuildUser)
        {
            Console.WriteLine("{0} {1} {2} {3} {4} {5} {6} {7}", TFSBuildController, TFSProject, TFSBuildDefinition, TFSBuildStatus, TFSBuildPriority, TFSBuildDateTime, ElapsedTime, TFSBuildUser);
        }

        public static string GetBuildAgent(IBuildDetail build)  //IQueuedBuild.Build
        {
            foreach (var child in build.Information.Nodes)
            {
                string AgentName = ShowChild(child, 1);
                if (!string.IsNullOrEmpty(AgentName))
                {
                    return AgentName;
                }
            }
            return string.Empty;
        }

        static string ShowChild(IBuildInformationNode node, int level)
        {
            string levelStr = new string(' ', level * 4);
            foreach (var field in node.Fields)
            {
                if (field.Key == "ReservedAgentName")
                {
                    return field.Value;
                }
            }

            foreach (var child in node.Children.Nodes)
            {
                string AgentName = ShowChild(child, level + 1);
                if (!string.IsNullOrEmpty(AgentName))
                {
                    return AgentName;
                }
            }
            return string.Empty;

        }

    }

}

Related Posts

Comments