Saturday, June 9, 2012

TFS Queries: Build agents status and build queue

For an intro on LinqPad and the TFS API please read this post
For the list of all the posts in this series please read this one

Context


Sometimes it's hard to figure out exactly why a build is pending and not running right after it was queued. It can be because the build agents are currently busy building other builds, or because build agents are not started or even because the windows services for the build agents are not running. Looking at the Build Explorer we can only see builds from one team project at a time which is not ideal when you have a lot of them.

Fortunately we can create a LinqPad query to get all this information in one report.

Required references


Microsoft.TeamFoundation.Build.Client
Microsoft.TeamFoundation.Client
Microsoft.TeamFoundation.Framework.Client
System.ServiceProcess

Query (C# Statements)

var tfs = TfsTeamProjectCollectionFactory
 .GetTeamProjectCollection(new Uri("http://localhost:8088/tfs"));

tfs.EnsureAuthenticated();

var buildServer = tfs.GetService<IBuildServer>();

var queryAgents = buildServer.QueryBuildAgents(buildServer.CreateBuildAgentSpec());
queryAgents.Agents
 .Select(x => new
 {
  x.MachineName,
  Controller = x.Controller.Name,
  x.Enabled,
  x.Status,
  x.QueueCount
 })
 .Dump("TFS build agents");

queryAgents.Agents
 .SelectMany(x => ServiceController.GetServices(x.ServiceHost.Name)
  .Where(s => s.ServiceName.StartsWith("TFSBuildServiceHost")))
 .Select(x => new
 {
  x.MachineName,
  x.ServiceName,
  x.Status
 })
 .Dump("Windows Services of build agents");
 
buildServer
 .QueryQueuedBuilds(buildServer.CreateBuildQueueSpec("*"))
 .QueuedBuilds
 .Select(x => new
 {
  x.TeamProject,
  BuildDefinition = x.BuildDefinition.Name,
  x.QueuePosition,
  x.QueueTime,
  x.Priority,
  x.CustomGetVersion,
  x.RequestedFor,
  x.Reason,
  x.Status
 })
 .OrderBy(x => x.QueuePosition)
 .Dump("Queued builds");


Result


We get a list of the build agents status. Then the state of the windows services running the build agents. Finally the full content of the build queue regardless of the team project.

TFS build agents

IEnumerable<> (2 items)
MachineName Controller Enabled Status QueueCount
Server4 Server4 -

Controller
False Available 0
Server1 Server1 -

Controller
True Available 3
3

Windows Services of build agents

IEnumerable<> (2 items)
MachineName ServiceName Status
Server4 TFSBuildServiceHost Running
Server1 TFSBuildServiceHost Running
Queued builds

IOrderedEnumerable<> (3 items)
TeamProject BuildDefinition QueuePosition QueueTime Priority CustomGetVersion RequestedFor Reason Status
Project1 CI 1 2012-06-01 16:58:32 Normal C3243 Pascal IndividualCI InProgress
Project1 Daily 2 2012-06-01 16:58:32 Normal C3243 Pascal IndividualCI Queued
Project1 Performance 3 2012-06-01 16:58:32 Normal C3243 Pascal IndividualCI Queued
6

1 comment:

Alexander Soutchilin said...

Great! Here is an adoption on TFS 2013

void Main()
{
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://mytfs:8080/tfs"));
tfs.EnsureAuthenticated();
var buildServer = tfs.GetService();
//buildServer.QueryBuildAgents(buildServer.CreateBuildAgentSpec()).Dump("tfs buld agents");

var queryAgents = buildServer.QueryBuildAgents(buildServer.CreateBuildAgentSpec());
queryAgents.Agents.Select(agent => new {
agent.ServiceHost.Controller.Name,
agent.FullPath,
agent.Status,
agent.Uri.AbsolutePath
}).Dump("Build Controller");
buildServer.QueryQueuedBuilds(buildServer.CreateBuildQueueSpec("*")).QueuedBuilds.Select(queuedBuild => new{
queuedBuild.BuildControllerUri.AbsolutePath,
queuedBuild.QueueTime,
queuedBuild.RequestedForDisplayName,
queuedBuild.Status,
queuedBuild.TeamProject, queuedBuild.BuildController.Name
}
).Dump("Queued Builds");
}