blob: 2a29ac533e2bbf591256ea8eaf0f034b15178ac3 [file] [log] [blame]
// DTM submission automation program
// Author: Michael Goldish <mgoldish@redhat.com>
// Based on sample code by Microsoft.
// Note: this program has only been tested with DTM version 1.5.
// It might fail to work with other versions, specifically because it uses
// a few undocumented methods/attributes.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.DistributedAutomation.DeviceSelection;
using Microsoft.DistributedAutomation.SqlDataStore;
namespace automate0
{
class AutoJob
{
// Wait for a machine to show up in the data store
static void FindMachine(IResourcePool rootPool, string machineName)
{
Console.WriteLine("Looking for machine '{0}'", machineName);
IResource machine = null;
while (true)
{
try
{
machine = rootPool.GetResourceByName(machineName);
}
catch (Exception e)
{
Console.WriteLine("Warning: " + e.Message);
}
// Make sure the machine is valid
if (machine != null &&
machine.OperatingSystem != null &&
machine.OperatingSystem.Length > 0 &&
machine.ProcessorArchitecture != null &&
machine.ProcessorArchitecture.Length > 0 &&
machine.GetDevices().Length > 0)
break;
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Client machine '{0}' found ({1}, {2})",
machineName, machine.OperatingSystem, machine.ProcessorArchitecture);
}
// Delete a machine pool if it exists
static void DeleteResourcePool(IDeviceScript script, string poolName)
{
while (true)
{
try
{
IResourcePool pool = script.GetResourcePoolByName(poolName);
if (pool != null)
script.DeleteResourcePool(pool);
break;
}
catch (Exception e)
{
Console.WriteLine("Warning: " + e.Message);
System.Threading.Thread.Sleep(1000);
}
}
}
// Set the machine's status to 'Reset' and optionally wait for it to become ready
static void ResetMachine(IResourcePool rootPool, string machineName, bool wait)
{
Console.WriteLine("Resetting machine '{0}'", machineName);
IResource machine;
while (true)
{
try
{
machine = rootPool.GetResourceByName(machineName);
machine.ChangeResourceStatus("Reset");
break;
}
catch (Exception e)
{
Console.WriteLine("Warning: " + e.Message);
System.Threading.Thread.Sleep(5000);
}
}
if (wait)
{
Console.WriteLine("Waiting for machine '{0}' to be ready", machineName);
while (machine.Status != "Ready")
{
try
{
machine = rootPool.GetResourceByName(machineName);
}
catch (Exception e)
{
Console.WriteLine("Warning: " + e.Message);
}
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Machine '{0}' is ready", machineName);
}
}
// Look for a device in a machine, and if not found, keep trying for 3 minutes
static IDevice GetDevice(IResourcePool rootPool, string machineName, string regexStr)
{
Regex deviceRegex = new Regex(regexStr, RegexOptions.IgnoreCase);
int numAttempts = 1;
DateTime endTime = DateTime.Now.AddSeconds(180);
while (DateTime.Now < endTime)
{
IResource machine = rootPool.GetResourceByName(machineName);
Console.WriteLine("Looking for device '{0}' in machine '{1}' (machine has {2} devices)",
regexStr, machineName, machine.GetDevices().Length);
foreach (IDevice d in machine.GetDevices())
{
if (deviceRegex.IsMatch(d.FriendlyName))
{
Console.WriteLine("Found device '{0}'", d.FriendlyName);
return d;
}
}
Console.WriteLine("Device not found");
if (numAttempts % 5 == 0)
ResetMachine(rootPool, machineName, true);
else
System.Threading.Thread.Sleep(5000);
numAttempts++;
}
Console.WriteLine("Error: device '{0}' not found", deviceRegex);
return null;
}
static int Main(string[] args)
{
if (args.Length < 5)
{
Console.WriteLine("Error: incorrect number of command line arguments");
Console.WriteLine("Usage: {0} serverName machinePoolName submissionName timeout machineName0 machineName1 ...",
System.Environment.GetCommandLineArgs()[0]);
return 1;
}
string serverName = args[0];
string machinePoolName = args[1];
string submissionName = args[2];
double timeout = Convert.ToDouble(args[3]);
List<string> machines = new List<string>();
for (int i = 4; i < args.Length; i++)
machines.Add(args[i]);
try
{
// Initialize DeviceScript and connect to data store
Console.WriteLine("Initializing DeviceScript object");
DeviceScript script = new DeviceScript();
Console.WriteLine("Connecting to data store");
script.ConnectToNamedDataStore(serverName);
// Wait for client machines to become available
IResourcePool rootPool = script.GetResourcePoolByName("$");
foreach (string machineName in machines)
FindMachine(rootPool, machineName);
// Delete the machine pool if it already exists
DeleteResourcePool(script, machinePoolName);
// Create the machine pool and add the client machines to it
// (this must be done because jobs cannot be scheduled for machines in the
// default pool)
try
{
script.CreateResourcePool(machinePoolName, rootPool.ResourcePoolId);
}
catch (Exception e)
{
Console.WriteLine("Warning: " + e.Message);
}
IResourcePool newPool = script.GetResourcePoolByName(machinePoolName);
foreach (string machineName in machines)
{
Console.WriteLine("Moving machine '{0}' to pool '{1}'", machineName, machinePoolName);
rootPool.GetResourceByName(machineName).ChangeResourcePool(newPool);
}
// Reset client machine
foreach (string machineName in machines)
ResetMachine(rootPool, machineName, true);
// Get requested device regex and look for a matching device in the first machine
Console.WriteLine("Device to test:");
IDevice device = GetDevice(rootPool, machines[0], Console.ReadLine());
if (device == null)
return 1;
// Get requested jobs regex
Console.WriteLine("Jobs to run:");
Regex jobRegex = new Regex(Console.ReadLine(), RegexOptions.IgnoreCase);
// Create a submission
Object[] existingSubmissions = script.GetSubmissionByName(submissionName);
if (existingSubmissions.Length > 0)
{
Console.WriteLine("Submission '{0}' already exists -- removing it",
submissionName);
script.DeleteSubmission(((ISubmission)existingSubmissions[0]).Id);
}
string hardwareId = device.InstanceId.Remove(device.InstanceId.LastIndexOf("\\"));
Console.WriteLine("Creating submission '{0}' (hardware ID: {1})", submissionName, hardwareId);
ISubmission submission = script.CreateHardwareSubmission(submissionName, newPool.ResourcePoolId, hardwareId);
// Set submission DeviceData
List<Object> deviceDataList = new List<Object>();
while (true)
{
ISubmissionDeviceData dd = script.CreateNewSubmissionDeviceData();
Console.WriteLine("DeviceData name:");
dd.Name = Console.ReadLine();
if (dd.Name.Length == 0)
break;
Console.WriteLine("DeviceData data:");
dd.Data = Console.ReadLine();
deviceDataList.Add(dd);
}
submission.SetDeviceData(deviceDataList.ToArray());
// Set submission descriptors
List<Object> descriptorList = new List<Object>();
while (true)
{
Console.WriteLine("Descriptor path:");
string descriptorPath = Console.ReadLine();
if (descriptorPath.Length == 0)
break;
descriptorList.Add(script.GetDescriptorByPath(descriptorPath));
}
submission.SetLogoDescriptors(descriptorList.ToArray());
// Set machine dimensions
foreach (string machineName in machines)
{
IResource machine = rootPool.GetResourceByName(machineName);
while (true)
{
Console.WriteLine("Dimension name ({0}):", machineName);
string dimName = Console.ReadLine();
if (dimName.Length == 0)
break;
Console.WriteLine("Dimension value ({0}):", machineName);
machine.SetDimension(dimName, Console.ReadLine());
}
// Set the WDKSubmissionId dimension for all machines
machine.SetDimension("WDKSubmissionId", submission.Id.ToString() + "_" + submission.Name);
}
// Get job parameters
List<string> paramNames = new List<string>();
List<string> paramValues = new List<string>();
foreach (string machineName in machines)
{
while (true)
{
Console.WriteLine("Parameter name ({0}):", machineName);
string paramName = Console.ReadLine();
if (paramName.Length == 0)
break;
Console.WriteLine("Device regex ({0}):", machineName);
IDevice d = GetDevice(rootPool, machineName, Console.ReadLine());
if (d == null)
return 1;
string deviceName = d.GetAttribute("name")[0].ToString();
Console.WriteLine("Setting parameter value to '{0}'", deviceName);
paramNames.Add(paramName);
paramValues.Add(deviceName);
}
}
// Find jobs that match the requested pattern
Console.WriteLine("Scheduling jobs:");
List<IJob> jobs = new List<IJob>();
foreach (IJob j in submission.GetJobs())
{
if (jobRegex.IsMatch(j.Name))
{
Console.WriteLine(" " + j.Name);
// Set job parameters
for (int i = 0; i < paramNames.Count; i++)
{
IParameter p = j.GetParameterByName(paramNames[i]);
if (p != null)
p.ScheduleValue = paramValues[i];
}
jobs.Add(j);
}
}
if (jobs.Count == 0)
{
Console.WriteLine("Error: no submission jobs match pattern '{0}'", jobRegex);
return 1;
}
// Create a schedule, add jobs to it and run it
ISchedule schedule = script.CreateNewSchedule();
foreach (IScheduleItem item in submission.ProcessJobs(jobs.ToArray()))
{
item.Device = device;
schedule.AddScheduleItem(item);
}
schedule.AddSubmission(submission);
schedule.SetResourcePool(newPool);
script.RunSchedule(schedule);
// Wait for jobs to complete
Console.WriteLine("Waiting for all jobs to complete (timeout={0}s)", timeout);
DateTime endTime = DateTime.Now.AddSeconds(timeout);
int numCompleted, numFailed;
do
{
System.Threading.Thread.Sleep(30000);
// Report results in a Python readable format and count completed and failed schedule jobs
numCompleted = numFailed = 0;
Console.WriteLine();
Console.WriteLine("---- [");
foreach (IResult r in schedule.GetResults())
{
if (r.ResultStatus != "InProgress") numCompleted++;
if (r.ResultStatus == "Investigate") numFailed++;
Console.WriteLine(" {");
Console.WriteLine(" 'id': {0}, 'job': r'''{1}''',", r.Job.Id, r.Job.Name);
Console.WriteLine(" 'logs': r'''{0}''',", r.LogLocation);
if (r.ResultStatus != "InProgress")
Console.WriteLine(" 'report': r'''{0}''',",
submission.GetSubmissionResultReport(r));
Console.WriteLine(" 'status': '{0}',", r.ResultStatus);
Console.WriteLine(" 'pass': {0}, 'fail': {1}, 'notrun': {2}, 'notapplicable': {3}",
r.Pass, r.Fail, r.NotRun, r.NotApplicable);
Console.WriteLine(" },");
}
Console.WriteLine("] ----");
} while (numCompleted < schedule.GetResults().Length && DateTime.Now < endTime);
Console.WriteLine();
// Cancel incomplete jobs
foreach (IResult r in schedule.GetResults())
if (r.ResultStatus == "InProgress")
r.Cancel();
// Reset the machines
foreach (string machineName in machines)
ResetMachine(rootPool, machineName, false);
// Report failures
if (numCompleted < schedule.GetResults().Length)
Console.WriteLine("Some jobs did not complete on time.");
if (numFailed > 0)
Console.WriteLine("Some jobs failed.");
if (numFailed > 0 || numCompleted < schedule.GetResults().Length)
return 1;
Console.WriteLine("All jobs completed.");
return 0;
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
return 1;
}
}
}
}