Condor SOAP Interface
From Ben's Writing
Contents |
Condor Configuration
To enable the Condor web-service, add the following lines to your configuration and restart Condor.
# Turn on SOAP functionality ENABLE_SOAP = TRUE # Enable the web-service ENABLE_WEB_SERVER = TRUE WEB_ROOT_DIR=$(RELEASE_DIR)/web
# Give everyone access... yikes! (you should probably not allow for # this in a production environment) ALLOW_SOAP = */* QUEUE_ALL_USERS_TRUSTED = TRUE
If for some reason the default port for the service is not acceptable, the following might also be helpful, although it does not seem to be documented—meaning either: (a), we hate the idea of having the option in there; (b), we take no responsibility if it disappears in future releases; or, (c), we've simply been hard at work coding, rather than documenting. I prefer to believe the latter, but you're the judge. Either way, this will let you put your Condor web-service on the port you desire.
# Set a well known port for the schedd to listen on SCHEDD_ARGS = -p 8080
You may also want to set the following:
HOSTALLOW_WRITE = *
This will help if you have been getting "Permission denied" errors after calling schedd.beginTransaction(30);.
In some rare cases it may be necessary to have Condor bind to a particular network interface as well. If this is the case, then adding the following will remedy the problem:
# Tell Condor to bind to a particular network interface, # instead of the first one it finds. NETWORK_INTERFACE = 192.168.67.42
Where 192.168.67.42 represents the network interface Condor listens on. Please change this to match your network topography.
Windows Configuration (using C#)
Creating C# source files from WSDL files
To generate code from WSDL files is relatively straight forward. At the time of writing, there are only two WSDLs worth using:
wsdl /nologo /sharetypes /namespace:Condor ^ file:/c:/condor/lib/webservice/condorCollector.wsdl ^ file:/c:/condor/lib/webservice/condorSchedd.wsdl ^ /out:Condor.cs
This will generate the C# interfaces to the web-service we just configured above. The WSDL files can be found in $(LOCAL_DIR)\lib\webservice. The /sharetypes option consolidates all the basic and shared functionality of the two WSDLs into one place. The result of this will be written to Condor.cs which will contain both the condorCollector and condorSchedd proxy classes in the Condor namespace (along with all the helper types).
Compiling
You have two alternatives here, you can create a standalone assembly or simply add the file your project. Both have their advantages and disadvantages. The stand alone assembly will only need to be compiled once, but it adds a dependency to your code. On the other hand, adding the source file to your project reduces the number of dependencies, at the cost of recompilation.
The Library
It is easy to create an assembly which can be used in any client applications with the C# compiler:
csc /target:library Condor.cs
The result is an assembly with all the proxy classes, which can be used in any C# application.
The Visual Studio Project
- You must first create a C# Console Application;
- Add a reference to the system interfaces System.Web.Services;
- Add the Condor.cs file to the project;
- Everything should compile cleanly now;
- In the C# Console Application's
Mainentry point, enter your code.
Example Code
Condor Version
using System;
using Condor;
namespace Condor {
public partial class condorCollector {
//
// Because the WSDL generated code created a partial class,
// we can add all sorts of helper function to the stock object.
// This constructor, for instance, takes the web-service host
// URL the user wants to connect to.
//
public condorCollector(string url) {
Url = url;
}
}
}
namespace CondorTest {
class CondorVersion {
static void Main(string[] args) {
try {
//
// Assuming we are running a Collector at this host,
// the following code will print both the version and
// platform strings.
//
using (condorCollector collector =
new condorCollector("http://example.com:8080"))
{
//
// Extract the basic Condor version information
//
StringAndStatus version = collector.getVersionString();
Console.WriteLine("Version : " + version.message);
StringAndStatus platform = collector.getPlatformString();
Console.WriteLine("Platform : " + platform.message);
}
} catch (System.Exception e) {
Console.WriteLine(e.ToString());
}
}
}
}
Basic Condor Queue
using System;
using System.Collections.Generic;
using Condor;
namespace CondorTest {
class CondorQ {
static void Main(string[] args) {
try {
//
// Assuming we are running a Schedd at this host,
// the following code will print both the version and
// platform strings.
//
using (condorSchedd schedd = new condorSchedd()) {
//
// Set the URL of the web-service host that we want
// to connect to.
//
schedd.Url = "http://example.com:8080";
//
// Convert what getJobAds() return to a dictionary
//
ClassAdStructArrayAndStatus adsAndStatus =
schedd.getJobAds(null, null);
var jobAds = new List<Dictionary<string, string>> ();
foreach (var classAd in adsAndStatus.classAdArray) {
var jobAd = new Dictionary<string, string> ();
foreach (var attr in classAd) {
if (!jobAd.ContainsKey(attr.name)) {
jobAd.Add(attr.name, attr.value);
}
}
if (jobAd.Count > 0) {
jobAds.Add(jobAd);
}
}
//
// List all the jobs in the queue
//
Console.WriteLine(" ID\tOWNER\t\tCMD");
foreach (var job in jobAds) {
Console.WriteLine("{0,3:D}.{1:D}\t{2}\t{3}",
job["ClusterId"], job["ProcId"],
job["Owner"], job["Cmd"]);
}
}
}
catch (System.Exception e) {
Console.WriteLine(e.ToString());
}
}
}
}
Condor Submit
using System;
using Condor;
using System.Collections.Generic;
namespace Condor {
public partial class ClassAdStructAttr {
public ClassAdStructAttr () {}
public ClassAdStructAttr(string name, ClassAdAttrType type, string value) {
this.name = name;
this.type = type;
this.value = value;
}
}
}
static class ListFromArray {
public static void FromArray<T>(this List<T> dst, IList<T> src) {
foreach (T item in src) { dst.Add(item); }
}
}
namespace CondorTest {
class CondorSubmit {
static void Main(string[] args) {
try {
//
// Assuming we are running a Schedd at this host,
// the following code will create and submit a job
//
using (condorSchedd schedd = new condorSchedd()) {
//
// Se the URL of the web-service host that we want
// to connect to.
//
schedd.Url = "http://example.com:8080";
//
// Create a transaction
//
TransactionAndStatus ts =
schedd.beginTransaction(30);
if (!ts.status.code.Equals(StatusCode.SUCCESS)) {
Console.WriteLine("Failed to start the transaction ({0}).",
ts.status.message);
return;
}
//
// Create a new cluster ID
//
IntAndStatus cluster =
schedd.newCluster(ts.transaction);
if (!cluster.status.code.Equals(StatusCode.SUCCESS)) {
Console.WriteLine("Failed to create new cluster ID ({0}).",
cluster.status.message);
return;
}
//
// Create a new job ID
//
IntAndStatus job =
schedd.newJob(ts.transaction, cluster.integer);
if (!job.status.code.Equals(StatusCode.SUCCESS)) {
Console.WriteLine("Failed to create new job ID ({0}).",
job.status.message);
return;
}
//
// Create a simple job that will sleep
// for 30 seconds.
//
var owner = "Ben Burnett";
var type = UniverseType.VANILLA;
var executable = "C:\\WINDOWS\\system32\\ping.exe";
var arguments = "-n 31 127.0.0.1 >nul 2>&1";
var requirements = "TRUE";
ClassAdStructAndStatus jobAd =
schedd.createJobTemplate(
cluster.integer,
job.integer,
owner,
type,
executable,
arguments,
requirements);
if (!jobAd.status.code.Equals(StatusCode.SUCCESS)) {
Console.WriteLine("Failed to create job ({0}).",
jobAd.status.message);
return;
}
//
// Add some more info into the ClassAd
//
var classAd = new List<ClassAdStructAttr> ();
classAd.FromArray(jobAd.classAd);
classAd.Add(
new ClassAdStructAttr(
"NTDomain",
ClassAdAttrType.STRINGATTR,
"BART"));
classAd.Add(
new ClassAdStructAttr(
"OpSys",
ClassAdAttrType.STRINGATTR,
"WINNT51"));
classAd.Add(
new ClassAdStructAttr(
"UserLog",
ClassAdAttrType.STRINGATTR,
"C:\\sleep.$(cluster).$(process).log"));
classAd.Add(
new ClassAdStructAttr(
"Output",
ClassAdAttrType.STRINGATTR,
"C:\\sleep.$(cluster).$(process).output.log"));
classAd.Add(
new ClassAdStructAttr(
"Err",
ClassAdAttrType.STRINGATTR,
"C:\\sleep.$(cluster).$(process).error.log"));
classAd.Add(
new ClassAdStructAttr(
"In",
ClassAdAttrType.STRINGATTR,
"NUL"));
//
// Dump the class ad to standard out
//
Console.WriteLine(classAd.Count);
classAd.Sort();
foreach ( var attribute in classAd ) {
Console.WriteLine(
attribute.name + " = " + attribute.value);
}
//
// Submit the job
//
RequirementsAndStatus result =
schedd.submit(
ts.transaction,
cluster.integer,
job.integer,
classAd.ToArray());
if (!result.status.code.Equals(StatusCode.SUCCESS)) {
Console.WriteLine("Failed to submit job ({0}).",
result.status.message);
return;
}
Console.WriteLine("Submit Status: "
+ result.status.message);
//
// Print any missing requirements
//
if (null != result.requirements) {
for (int i = 0; i < result.requirements.Length; ++i) {
Console.WriteLine(result.requirements[i].);
}
}
//
// Commit the work
//
Status status =
schedd.commitTransaction(ts.transaction);
Console.WriteLine("Transaction Status: " + status.message);
}
}
catch (System.Exception e) {
Console.WriteLine(e.ToString());
}
}
}
}