Cloning a Job

If you want to run a job multiple times (you cannot rerun a finished job or move it back to the configuring state) or if you want to create variations of a job, call the IScheduler::CloneJob method to create a job that is a copy of the specified job.

The CloneJob method copies all the tasks (includes the instances for parametric tasks) and a subset of the job and task property values. For a list of the properties that the method copies, see the Remarks section for the CloneJob method.

You can clone a job that is in any state; however, only the owner of the job can clone the job.

If a job fails, you can either clone the job (if you want to save its state) or you can call the IScheduler::ConfigureJob method to move the job back to the configuring state to fix the cause of the error.

The following C# example shows how to clone a job, change the properties on the existing tasks, add a new task, and submit the new job.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Hpc.Scheduler;
using Microsoft.Hpc.Scheduler.Properties;

namespace CloneJob
{
    class Program
    {
        private static IScheduler m_scheduler = null;

        static void Main(string[] args)
        {
            ISchedulerJob clonedJob = null;
            int jobId = 0;

            try
            {
                m_scheduler = new Scheduler();
                m_scheduler.Connect("localhost");

                jobId = CreateJob();
                PrintTasks(m_scheduler.OpenJob(jobId));

                clonedJob = m_scheduler.CloneJob(jobId);
                PrintTasks(m_scheduler.OpenJob(clonedJob.Id));

                UpdateClonedJob(clonedJob);
                PrintTasks(m_scheduler.OpenJob(clonedJob.Id));

                m_scheduler.SubmitJob(clonedJob, @"<USERNAMEGOESHERE>", null);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        // Creates a job, adds a task and a parametric task to the job, and
        // then submits the job. 
        private static int CreateJob()
        {
            ISchedulerJob job = null;
            ISchedulerTask task = null;

            job = m_scheduler.CreateJob();

            // Create a parametric task
            task = job.CreateTask();
            task.CommandLine = @"echo *";
            task.IsParametric = true;
            task.StartValue = 1;
            task.EndValue = 5;
            task.IncrementValue = 1;
            job.AddTask(task);

            // Create a task
            task = job.CreateTask();
            task.CommandLine = @"ping <COMPUTERNAMEGOESHERE>";
            job.AddTask(task);

            m_scheduler.SubmitJob(job, @"<USERNAMEGOESHERE>", null);

            return job.Id;
        }

        // Print the tasks IDs of the tasks in the job. Include StdOut
        // to show the update to the tasks.
        private static void PrintTasks(ISchedulerJob job)
        {
            Console.WriteLine("Tasks for job " + job.Id);

            foreach (ISchedulerTask task in job.GetTaskList(null, null, true))
            {
                Console.WriteLine("id({0}), instance({1}), job({2})",
                    task.TaskId.JobTaskId, task.TaskId.InstanceId, task.TaskId.ParentJobId);

                Console.WriteLine("\tcommand: " + task.CommandLine);
                Console.WriteLine("\tstdout: " + task.StdOutFilePath);
            }

            Console.WriteLine();
        }

        // Update the cloned job. Add StdOut path to capture output from tasks.
        // Add a new task to the job.
        private static void UpdateClonedJob(ISchedulerJob job)
        {
            ISchedulerTask task = null;
            ITaskId taskId = null;

            // Get the parametric task. Update the start value and
            // set the stdout path.
            taskId = m_scheduler.CreateTaskId(1);
            task = job.OpenTask((TaskId)taskId);
            task.StartValue = 3;
            task.StdOutFilePath = @"<PATHGOESHERE>\echo_inst*.txt";
            task.Commit();

            // Add a new task to the job.
            task = job.CreateTask();
            task.CommandLine = @"echo 'new task'";
            task.StdOutFilePath = @"<PATHGOESHERE>\new.txt";
            job.AddTask(task);
        }
    }
}

The following C++ example shows how to clone a job, change the properties on the existing tasks, add a new task, and submit the new job.

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


// The Microsoft.Hpc.Scheduler.tlb and Microsoft.Hpc.Scheduler.Properties.tlb type
// libraries are included in the Microsoft HPC Pack 2008 SDK. The type libraries are
// located in the "Microsoft HPC Pack 2008 SDK\Lib\i386" or \amd64 folder. Include the rename 
// attributes to avoid name collisions.
#import <Microsoft.Hpc.Scheduler.tlb> named_guids no_namespace raw_interfaces_only \
    rename("SetEnvironmentVariable","SetHpcEnvironmentVariable") \
    rename("AddJob", "AddHpcJob")
#import <Microsoft.Hpc.Scheduler.Properties.tlb> named_guids no_namespace raw_interfaces_only 


IScheduler* g_pScheduler = NULL;

long CreateJob(void);
void PrintTasks(int JobId);
void UpdateClonedJob(int JobId);

void wmain(int argc, WCHAR *argv[])
{
    HRESULT hr = S_OK;
    ISchedulerJob* pClonedJob = NULL;
    long JobId = 0;
    long ClonedJobId = 0;

    // Use the apartment-threaded model to ensure the code is thread safe.
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    // Get an instance of the Scheduler object. 
    hr = CoCreateInstance( __uuidof(Scheduler), // CLSID_Scheduler, 
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           __uuidof(IScheduler), // IID_IScheduler, 
                           reinterpret_cast<void **> (&g_pScheduler) );

    if (FAILED(hr))
    {
        wprintf(L"CoCreateInstance(IScheduler) failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = g_pScheduler->Connect(_bstr_t(L"localhost"));
    if (FAILED(hr))
    {
        wprintf(L"Unable to connect to localhost. Failed with 0x%x.\n", hr);
        goto cleanup;
    }

    wprintf(L"Connected to localhost\n");

    JobId = CreateJob();
    if (JobId)
    {
        PrintTasks(JobId);

        hr = g_pScheduler->CloneJob(JobId, &pClonedJob);
        if (FAILED(hr))
        {
            wprintf(L"g_pScheduler->CloneJob failed with 0x%x.\n", hr);
            goto cleanup;
        }

        hr = pClonedJob->get_Id(&ClonedJobId);
        PrintTasks(ClonedJobId);

        UpdateClonedJob(ClonedJobId);
        PrintTasks(ClonedJobId);
        
        hr = g_pScheduler->SubmitJobById(ClonedJobId, _bstr_t(L"<USERNAMEGOESHERE>"), NULL);
        if (FAILED(hr))
        {
            wprintf(L"g_pScheduler->SubmitJobById failed with 0x%x.\n", hr);
            goto cleanup;
        }
    }

cleanup:

    // Before exiting, release your instance of IScheduler.
    if (g_pScheduler)
        g_pScheduler->Release();

    if (pClonedJob)
        pClonedJob->Release();

    CoUninitialize();
}

long CreateJob(void)
{
    HRESULT hr = S_OK;
    ISchedulerJob* pJob = NULL;
    ISchedulerTask* pTask = NULL;
    long JobId = 0;

    hr = g_pScheduler->CreateJob(&pJob);
    if (FAILED(hr))
    {
        wprintf(L"g_pScheduler->CreateJob failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->CreateTask(&pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->CreateTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_CommandLine(_bstr_t(L"echo *"));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_CommandLine failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_IsParametric(VARIANT_TRUE);
    hr = pTask->put_StartValue(1);
    hr = pTask->put_EndValue(5);
    hr = pTask->put_IncrementValue(1);

    hr = pJob->AddTask(pTask);
    if (FAILED(hr))
    {
        wprintf(L"pTask->AddTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    pTask->Release();
    pTask = NULL;

    hr = pJob->CreateTask(&pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->CreateTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_CommandLine(_bstr_t(L"ping <COMPUTERNAMEGOESHERE>"));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_CommandLine failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->AddTask(pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->AddTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = g_pScheduler->SubmitJob(pJob, _bstr_t("<USERNAMEGOESHERE>"), NULL);
    if (FAILED(hr))
    {
        wprintf(L"g_pScheduler->SubmitJob failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->get_Id(&JobId);

cleanup:

    if (pJob)
        pJob->Release();

    if (pTask)
        pTask->Release();

    return JobId;
}

void PrintTasks(int JobId)
{
    HRESULT hr = S_OK;
    ISchedulerJob* pJob = NULL;
    ISchedulerTask* pTask = NULL;
    ISchedulerCollection* pCollection = NULL;
    ITaskId* pTaskId = NULL;
    _variant_t var;
    long JobTaskId = 0;
    long InstanceId = 0;
    long Count = 0;
    _bstr_t bstrCommand;
    _bstr_t bstrStdOut;

    wprintf(L"Tasks for job %d\n", JobId);

    hr = g_pScheduler->OpenJob(JobId, &pJob);
    if (FAILED(hr))
    {
        wprintf(L"g_pScheduler->OpenJob failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->GetTaskList(NULL, NULL, VARIANT_TRUE, &pCollection);
    if (FAILED(hr))
    {
        wprintf(L"pJob->GetTaskList failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pCollection->get_Count(&Count);

    for (long i = 0; i < Count; i++)
    {
        hr = pCollection->get_Item(i, &var);
        if (FAILED(hr))
        {
            wprintf(L"pCollection->get_Item failed with 0x%x.\n", hr);
            goto cleanup;
        }

        hr = var.pdispVal->QueryInterface(IID_ISchedulerTask, reinterpret_cast<void**>(&pTask));

        hr = pTask->get_TaskId(&pTaskId);
        if (FAILED(hr))
        {
            wprintf(L"pTask->get_TaskId failed with 0x%x.\n", hr);
            goto cleanup;
        }

        hr = pTaskId->get_JobTaskId(&JobTaskId);
        hr = pTaskId->get_InstanceId(&InstanceId);

        wprintf(L"id(%d), instance(%d), job(%d)\n", JobTaskId, InstanceId, JobId);

        hr = pTask->get_CommandLine(&(bstrCommand.GetBSTR()));
        if (FAILED(hr))
        {
            wprintf(L"pTask->get_CommandLine failed with 0x%x.\n", hr);
            goto cleanup;
        }

        hr = pTask->get_StdOutFilePath(&(bstrStdOut.GetBSTR()));
        if (FAILED(hr))
        {
            wprintf(L"pTask->get_StdOutFilePath failed with 0x%x.\n", hr);
            goto cleanup;
        }

        wprintf(L"\tCommand: %s\n\tStdOut: %s\n", bstrCommand.GetBSTR(), bstrStdOut.GetBSTR());

        pTask->Release();
        pTask = NULL;
        pTaskId->Release();
        pTaskId = NULL;
    }

    wprintf(L"\n");

cleanup:

    if (pJob)
        pJob->Release();

    if (pTask)
        pTask->Release();

    if (pTaskId)
        pTaskId->Release();

    if (pCollection)
        pCollection->Release();
}

void UpdateClonedJob(int JobId)
{
    HRESULT hr = S_OK;
    ISchedulerJob* pJob = NULL;
    ISchedulerTask* pTask = NULL;
    ISchedulerCollection* pCollection = NULL;
    ITaskId* pTaskId = NULL;

    hr = g_pScheduler->OpenJob(JobId, &pJob);
    if (FAILED(hr))
    {
        wprintf(L"g_pScheduler->OpenJob failed with 0x%x.\n", hr);
        goto cleanup;
    }

    // Get the parametric task. Change the start
    // value and set the stdout path.
    hr = g_pScheduler->CreateTaskId(1, &pTaskId);
    if (FAILED(hr))
    {
        wprintf(L"g_pScheduler->CreateTaskId failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->OpenTask(pTaskId, &pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->OpenTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    pTaskId->Release();
    pTaskId = NULL;

    hr = pTask->put_StartValue(3);
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_StartValue failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_StdOutFilePath(_bstr_t(L"<FULLPATHGOESHERE>\\echo_inst*.txt"));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_StdOutFilePath failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->Commit();
    if (FAILED(hr))
    {
        wprintf(L"pTask->Commit failed with 0x%x.\n", hr);
        goto cleanup;
    }

    pTask->Release();
    pTask = NULL;

    // Add a new task to the job.
    hr = pJob->CreateTask(&pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->CreateTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_CommandLine(_bstr_t(L"echo \"new task\""));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_CommandLine failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pTask->put_StdOutFilePath(_bstr_t(L"<FULLPATHGOESHERE>\\new.txt"));
    if (FAILED(hr))
    {
        wprintf(L"pTask->put_StdOutFilePath failed with 0x%x.\n", hr);
        goto cleanup;
    }

    hr = pJob->AddTask(pTask);
    if (FAILED(hr))
    {
        wprintf(L"pJob->AddTask failed with 0x%x.\n", hr);
        goto cleanup;
    }

cleanup:

    if (pJob)
        pJob->Release();

    if (pTask)
        pTask->Release();

    if (pTaskId)
        pTaskId->Release();
}