A Complete Example

The following is a complete example that shows how to use the Compute Cluster Pack (CCP) API to connect to a cluster, create a job, add a task to the job, and add the job to the scheduling queue.

Note that there are several locations in the example that you will need to provide cluster-specific information before compiling the code.

///////////////////////////////////////////////////////////////////////////////
//
// This example shows how to connect to a cluster and schedule a job.
// There are several locations in the code that you will need to provide
// cluster-specific information before compiling the code.
//
// Copyright (C) Microsoft.  All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
///////////////////////////////////////////////////////////////////////////////


#define _WIN32_WINNT  0x0500

#include <windows.h>
#include <stdio.h>


// The Ccpapi.tlb type library is included in the CCP SDK and is located in 
// the Microsoft Compute Cluster Pack\Lib\i386 or \amd64 folder. Copy the type
// library to your project. When you compile your source code, the directive
// will create a Ccpapi.tlh that contains the interface definitions. The 
// rename attributes are used to prevent name collisions.
#import "ccpapi.tlb" named_guids no_namespace raw_interfaces_only \
  rename("SetEnvironmentVariable","SetEnvVar") \
  rename("GetJob", "GetSingleJob") \
  rename("AddJob", "AddSingleJob")


BOOL ConnectToCluster(ICluster* pCluster, LPWSTR pClusterName);
void PrintClusterMessage(ICluster* pCluster);
void PrintJobMessage(IJob* pJob);
void PrintTaskMessage(ITask* pTask);
BOOL SetJobTerms(IJob* pJob);
BOOL AddTaskToJob(ICluster* pCluster, IJob* pJob);
BOOL SetTaskTerms(ITask* pTask);


void main(void)
{
  HRESULT hr = S_OK;
  ICluster* pCluster = NULL;
  IJob* pJob = NULL;
  long JobId = 0;

  // CCP is not thread safe. You should use the apartment model or 
  // ensure thread safety yourself.
  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

  // Create the Cluster object. 
  hr = CoCreateInstance( __uuidof(Cluster),
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         __uuidof(ICluster),
                         reinterpret_cast<void **> (&pCluster) );

  if (FAILED(hr))
  {
    wprintf(L"Failed to create Cluster object, hr=0x%x.\n", hr);
    return;
  }

  // Try connecting to the head node of the cluster. If successful,
  // create the job, set the job terms, add a task to the job, and 
  // then add the job to the scheduling queue.
  if (ConnectToCluster(pCluster, L"<HEADNODENAMEGOESHERE>"))
  {
    hr = pCluster->CreateJob(&pJob);
    if (SUCCEEDED(hr))
    {
      if (SetJobTerms(pJob))
      {
        if (AddTaskToJob(pCluster, pJob))
        {
          // If the calling user's credentials are not cached, the user will be
          // asked to provide them.
          hr = pCluster->QueueJob(pJob, NULL, NULL, VARIANT_TRUE, 0, &JobId);
          if (SUCCEEDED(hr))
          {
            wprintf(L"Added job %d to scheduling queue.\n", JobId);
          }
          else
          {
            wprintf(L"pCluster->QueueJob failed.\n");
            PrintClusterMessage(pCluster);
          }
        }
      }

      pJob->Release();
    }
    else
    {
      wprintf(L"pCluster->CreateJob failed.\n");
      PrintClusterMessage(pCluster);
    }
  }

  pCluster->Release();
  CoUninitialize();
}


// Connect to the cluster's head node. Set pClusterName to the 
// computer name of the head node.
BOOL ConnectToCluster(ICluster* pCluster, LPWSTR pClusterName)
{
  HRESULT hr = S_OK;

  // Connect to the cluster. 
  hr = pCluster->Connect(_bstr_t(pClusterName));
  if (FAILED(hr))
  {
    wprintf(L"Failed to connect to cluster, %s.\n", pClusterName);
    PrintClusterMessage(pCluster);
    return FALSE;
  }

  wprintf(L"Connected to cluster, %s.\n", pClusterName);

  return TRUE;
}


// When you create a job, default job terms are applied to the job.
// This function changes a couple of the default terms. This example
// hard codes the values, but you would probably get the term values
// from a UI or from console arguments.
BOOL SetJobTerms(IJob* pJob)
{
  HRESULT hr = S_OK;

  // Run the tasks on this node. If you specified multiple nodes, 
  // the task could run on any of them unless you used ITask::put_RequiredNodes
  // to limit the nodes on which a task could run.
  hr = pJob->put_AskedNodes(_bstr_t(L"<ENTERSINGLENODEHERE>"));
  if (FAILED(hr))
  {
    wprintf(L"pJob->put_AskedNodes failed.\n");
    PrintJobMessage(pJob);
    return FALSE;
  }

  hr = pJob->put_Name(_bstr_t(L"Sample Job"));
  if (FAILED(hr))
  {
    wprintf(L"pJob->put_Name failed.\n");
    PrintJobMessage(pJob);
    return FALSE;
  }

  // Try not to overstate the importance of the job.
  hr = pJob->put_Priority(JobPriority_BelowNormal);
  if (FAILED(hr))
  {
    wprintf(L"pJob->put_Priority failed.\n");
    PrintJobMessage(pJob);
    return FALSE;
  }

  // You should try to specify the maximum time that the job will run. 
  // This will help the scheduler more accurately allocate resources.
  hr = pJob->put_Runtime(_bstr_t(L"00:00:01"));
  if (FAILED(hr))
  {
    wprintf(L"pJob->put_Runtime failed.\n");
    PrintJobMessage(pJob);
    return FALSE;
  }

  // Set extended terms for the job, if the activation or submission filter
  // requires them.
  hr = pJob->SetExtendedJobTerm(_bstr_t(L"MyTerm"), _bstr_t(L"TermValue"));
  if (FAILED(hr))
  {
    wprintf(L"pJob->SetExtendedJobTerm failed.\n");
    PrintJobMessage(pJob);
    return FALSE;
  }

  return TRUE;
}


// This function creates the task object, sets its terms, and
// then adds the task to the job.
BOOL AddTaskToJob(ICluster* pCluster, IJob* pJob)
{
  HRESULT hr = S_OK;
  ITask* pTask = NULL;
  BOOL fSuccess = FALSE;

  hr = pCluster->CreateTask(&pTask);
  if (SUCCEEDED(hr))
  {
    if (SetTaskTerms(pTask))
    {
      hr = pJob->AddTask(pTask);
      if (SUCCEEDED(hr))
      {
        fSuccess = TRUE;
      }
      else
      {
        wprintf(L"pJob->AddTask failed.\n");
        PrintJobMessage(pJob);
      }
    }

    pTask->Release();
  }
  else
  {
    wprintf(L"pCluster->CreateTask failed.\n");
    PrintClusterMessage(pCluster);
  }

  return fSuccess;
}


// When you create a task, default task terms are applied to the task.
// This function changes a couple of the default terms. This example
// hard codes the values, but you would probably get the term values
// from a UI or from console arguments.
BOOL SetTaskTerms(ITask* pTask)
{
  HRESULT hr = S_OK;

  hr = pTask->put_Name(_bstr_t(L"Sample Task"));
  if (FAILED(hr))
  {
    wprintf(L"pTask->put_Name failed.\n");
    PrintTaskMessage(pTask);
    return FALSE;
  }

  // The command line is required. This example captures the output
  // from dir.exe.
  hr = pTask->put_CommandLine(_bstr_t(L"dir %windir%\\system32\\setup"));
  if (FAILED(hr))
  {
    wprintf(L"pTask->put_CommandLine failed.\n");
    PrintTaskMessage(pTask);
    return FALSE;
  }

  // To get the output from the command, redirect stdout and stderr.
  // In this example, the output will go to the user's Documents and Settings 
  // folder on the single node specified in IJob::put_AskedNodes. If
  // you didn't specify the node, the task could run on any node in the cluster
  // and the output would end up on that node. You could also create 
  // a share and write to that share only.
  hr = pTask->put_Stdout(_bstr_t(L"%USERPROFILE%\\dirOutput.txt"));
  if (FAILED(hr))
  {
    wprintf(L"pTask->put_Stdout failed.\n");
    PrintTaskMessage(pTask);
    return FALSE;
  }

  hr = pTask->put_Stderr(_bstr_t(L"%USERPROFILE%\\dirError.txt"));
  if (FAILED(hr))
  {
    wprintf(L"pTask->put_Stderr failed.\n");
    PrintTaskMessage(pTask);
    return FALSE;
  }

  // Because you set the Runtime term on the job, you should also 
  // set it on the task. If the task exceeds this value, the
  // task will fail. If you leave the Runtime value set to 
  // Infinite (the default) and it exceeds the job's Runtime 
  // value, the task will also fail.
  hr = pTask->put_Runtime(_bstr_t(L"00:00:01"));
  if (FAILED(hr))
  {
    wprintf(L"pTask->put_Runtime failed.\n");
    PrintTaskMessage(pTask);
    return FALSE;
  }

  return TRUE;
}


// Call this function to print the message associated with an
// ICluster method failure.
void PrintClusterMessage(ICluster* pCluster)
{
  HRESULT hr = S_OK;
  BSTR bstrMessage = NULL;

  hr = pCluster->get_ErrorMessage(&bstrMessage);
  if (SUCCEEDED(hr))
  {
    wprintf(L"%s\n", bstrMessage);
    SysFreeString(bstrMessage);
  }
  else
  {
    wprintf(L"pCluster->get_ErrorMessage failed.\n");
  }
}


// Call this function to print the message associated with an
// IJob method failure. You can also use this function 
// to print the message specified when the job was canceled.
void PrintJobMessage(IJob* pJob)
{
  HRESULT hr = S_OK;
  BSTR bstrMessage = NULL;

  hr = pJob->get_ErrorMessage(&bstrMessage);
  if (SUCCEEDED(hr))
  {
    wprintf(L"%s\n", bstrMessage);
    SysFreeString(bstrMessage);
  }
  else
  {
    wprintf(L"pJob->get_ErrorMessage failed.\n");
  }
}

// Call this function to print the message associated with an
// ITask method failure or run-time error. You can also use this function 
// to print the message specified when the task was canceled.
void PrintTaskMessage(ITask* pTask)
{
  HRESULT hr = S_OK;
  BSTR bstrMessage = NULL;

  hr = pTask->get_ErrorMessage(&bstrMessage);
  if (SUCCEEDED(hr))
  {
    wprintf(L"%s\n", bstrMessage);
    SysFreeString(bstrMessage);
  }
  else
  {
    wprintf(L"pTask->get_ErrorMessage failed.\n");
  }
}
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.ComputeCluster;    // Reference ccpapi.dll

namespace Complete
{
    class Program
    {
        static void Main(string[] args)
        {
            ICluster cluster = new Cluster();
            IJob job = null;
            ITask task = null;

            try
            {
                cluster.Connect("localhost");

                job = cluster.CreateJob();
                job.Name = "My Job";

                task = cluster.CreateTask();
                task.CommandLine = "dir %windir%";
                task.Stdout = @"%userprofile%\windir_stdout.txt";
                task.Stderr = @"%userprofile%\windir_stderr.txt";

                job.AddTask(task);

                cluster.QueueJob(job, null, null, true, 0);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            cluster.Dispose();
        }
    }
}

Using CCP