DataStageジョブのステータスを取得するプログラムをDSAPIで作成してみた

# はじめに
DataStageジョブのステータスはdsjob -jobinfoで取得できますが、ジョブごとにdsjobを実行する必要がありジョブ数が多いと時間がかかる。そこで、プロジェクト内の全てのジョブのステータスを一括して取得するプログラムをDSAPI(DataStage API)を使用してC言語で作成してみた。

# 環境
環境は以下の通り。
RHEL: 8.8
InformationServer : 11.7.1.4
gcc : 8.5
　
InformationServerは、/opt/IBM/InformationServer にインストールした。

DSAPIを使用したプログラムでは、ヘッダファイル $DSHOME/include/dsapi.h が必要になるので、プログラムの先頭で、`#include "dsapi.h"` をインクルードした。
コンパイルでは、$DSHOME/lib/libvmdsapi.so が必要になるので、プログラムのコンパイルは以下のようになる。

. /opt/IBM/InformationServer/Server/DSEngine/dsenv
DSINC=\\$DSHOME/include
DSLIB=\\$DSHOME/lib

gcc -o プログラム プログラム.c -I\\$DSINC -L\\$DSLIB -lvmdsapi

コンパイルしたプログラムの実行はdsjobが実行できる環境と同じ。


# プログラムの作成
今回は、dsjob -jobinfo dstage1 Test1 で出力される情報のうち、
１．ジョブ状況
２．ジョブ開始時刻
３．前回の実行時間
の情報を取り出して標準出力に出力するプログラムを作成した。

\\$ \\$DSHOME/bin/dsjob -jobinfo dstage1 Test1
__ジョブ状況      : 正常実行 (1)__
ジョブ・コントローラー  : なし
__ジョブ開始時刻  : Tue Mar 28 23:46:10 2023__
ジョブ・ウェーブ番号    : 3
ユーザー状況    : なし
ジョブ制御      : 0
暫定の状況      : 実行されていません (99)
呼び出し ID     : なし
__前回の実行時間  : Tue Mar 28 23:46:10 2023__
ジョブ・プロセス ID     : 0
呼び出しリスト  : Test1
ジョブ再開可能  : 0

状況コード = 0
$

コードはこんな感じで作成した。
```c:dsjobinfo.c
/*
 *
 * . /opt/IBM/InformationServer/Server/DSEngine/dsenv
 *
 * DSINC=$DSHOME/include
 * DSLIB=$DSHOME/lib
 *
 * gcc -o dsjobinfo dsjobinfo.c -I$DSINC -L$DSLIB -lvmdsapi
 *
 * command line :
 *      <prog> -domain <domain> -server <server>
 *                      -user <username> -password <passord> [project]
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dsapi.h"

#define MAXPRJS 128     /* max number of projects */
#define MAXJOBS 1024    /* max number of jobs on a project */
#define LEN32 32        /* 32 byte */

#define GETLIST(str, ary, max, num)     \
{                               \
        int d;                  \
                                \
        for(d = 0; d < max; d++) ary[d] = NULL;         \
                                                        \
        d = 0;                                          \
        while(*str != '\0')                             \
        {                                               \
                ary[d] = (char *)malloc(strlen(str) + 1);       \
                strcpy(ary[d], str);                            \
                str += (strlen(str) + 1);                       \
                d++;                                            \
        }                                                       \
        num = d;                                                \
}                                                               \

DSPROJECT hProj;
DSPROJECTINFO pInfo;
DSJOB hJob;
DSJOBINFO jobInfo;

char *projs[MAXPRJS];   /* array for project name */
int pnum = 0;           /* the number of projects */

static void setMSG( char *tim, char *str );
static void getJobInfo( char *jname, int cpnum );


int main(int argc, char *argv[])
{
        int pstat;
        int i;
        char *plist;

        char *domain = NULL;
        char *server = NULL;
        char *user = NULL;
        char *password = NULL;
        char *project = NULL;

/*
 * Command line :
 *      <prog> -domain <domain> -server <server>
 *                      -user <username> -password <passord> [project]
 *                      
 */

        if (argc != 9 && argc != 10)
                goto Error;

        if (strcmp(argv[1], "-domain") == 0)
        {
                if (strcmp(argv[2], "NONE") != 0)
                        domain = argv[2];
        } else
                goto Error;

        if (strcmp(argv[3], "-server") == 0)
                server = argv[4];
        else
                goto Error;

        if (strcmp(argv[5], "-user") == 0)
                user = argv[6];
        else
                goto Error;

        if (strcmp(argv[7], "-password") == 0)
                password = argv[8];
        else
                goto Error;

        /*  Set projet name to projs when args have projet name */
        if (argc == 10)
        {
                projs[0] = (char *)malloc(strlen(argv[9]) + 1);
                strcpy(projs[0], argv[9]);

                pnum = 1;
        }

        /*  Set connnecion properties */
        DSSetServerParams(domain, user, password, server);

        /* Get project list as args don't have project name */
        if (pnum == 0)
        {
                if ((plist = DSGetProjectList()) == NULL)
                {
                        printf("Unable to get project list\n");
                        printf("error=%d\n", DSGetLastError());
                        goto Error1;
                } else {
                        /* Get project list */
                        GETLIST(plist, projs, MAXPRJS, pnum);
                }
        }

        for(i = 0; i < pnum; i++)
        {
                hProj = DSOpenProject(projs[i]);
                if (hProj == NULL)
                {
                        printf("Fail to open project\n");
                        printf("Error code = %d\n", DSGetLastError());
                }
                else
                {
                        /* Get job list in a project */
                        pstat = DSGetProjectInfo(hProj, DSJ_JOBLIST, &pInfo);
                        if (pstat == DSJE_NOT_AVAILABLE)
                                printf("%s,there may be no job in a project\n",projs[i]);
                        else if (pstat == DSJE_NOERROR)
                                /* Get status, start/last time for each job */
                                getJobInfo(pInfo.info.jobList, i);
                }
                DSCloseProject(hProj);
        }

        return 0;

Error:
        printf("Command line : \n");
        printf("\t%s -domain <domain:port> -server <enigne>\n", argv[0]);
        printf("\t\t-user <username> -password <password> [project]\n");
        printf("\t(*1) connect to DSEnigne directly with -domain NONE\n");
        printf("\t(*2) jobs in all projects are checked when project is not set in args\n");

Error1:
        return 1;

}


static void setMSG( char *tim, char *str )
{
        int i;
        i = strlen(str);
        strncpy(tim, str, i);
        tim[i] = 0;
}


static void getJobInfo( char *jname , int cpnum )
{
    int stat;
    int i, num, js;

    char stime[LEN32], ltime[LEN32];
    char *jnm[MAXJOBS]; /* job array */


    /* Put job list in array(jnm) */
    GETLIST(jname, jnm, MAXJOBS, num);

    for(i=0; i<num; i++)
    {
        hJob = DSOpenJob(hProj, jnm[i]);
        if (hJob == NULL)
                printf("%s,%s,OPEN ERROR\n", projs[cpnum], jnm[i]);
        else
        {
                /* Get status */
                stat = DSGetJobInfo(hJob, DSJ_JOBSTATUS, &jobInfo);
                if (stat == DSJE_NOT_AVAILABLE)
                        js = 9999;
                else if (stat != DSJE_NOERROR)
                        js = 99999;
                else
                        js = jobInfo.info.jobStatus;

                /* Get Start timestamp */
                stat = DSGetJobInfo(hJob, DSJ_JOBSTARTTIMESTAMP, &jobInfo);
                if (stat == DSJE_NOT_AVAILABLE)
                {
                        setMSG(stime, "NONE(no available)");
                }
                else if (stat != DSJE_NOERROR)
                {
                        setMSG(stime, "NONE(get error)");
                }
                else
                {
                        strncpy(stime, ctime(&(jobInfo.info.jobStartTime)), strlen(ctime(&(jobInfo.info.jobStartTime))) );
                        stime[strlen(ctime(&(jobInfo.info.jobStartTime))) - 1] = 0;
                }

                /* Get Last timestamp */
                stat = DSGetJobInfo(hJob, DSJ_JOBLASTTIMESTAMP, &jobInfo);
                if (stat == DSJE_NOT_AVAILABLE)
                {
                        setMSG(ltime, "NONE(no availabe)");
                }
                else if (stat != DSJE_NOERROR)
                {
                        setMSG(ltime, "NONE(get error)");
                }
                else
                {
                        strncpy(ltime, ctime(&(jobInfo.info.jobLastTime)), strlen(ctime(&(jobInfo.info.jobLastTime))) );
                        ltime[strlen(ctime(&(jobInfo.info.jobLastTime))) - 1] = 0;
                }

                /* project, job, status, start time, last time */
                printf("%s,%s,%d,%s,%s\n", projs[cpnum], jnm[i], js, stime, ltime);

                DSCloseJob(hJob);
        }
    }
}
```

# コンパイル・実行
### コンパイル
\\$ . /opt/IBM/InformationServer/Server/DSEngine/dsenv
\\$ DSINC=\\$DSHONE/include
\\$ DSLIB=\\$DSHOME/lib
\\$ gcc -o dsjobinfo dsjobinfo.c -I\\$DSINC -L\\$DSLIB -lvmdsapi
\\$

### 実行
実行結果はこんな感じ。
\\$ ./dsjobinfo -domain NONE -server localhost -user dsadm -password dsadm 
dstage1,Test0,1,Mon Jul  1 12:03:47 2024,Mon Jul  1 12:03:47 2024
dstage1,TestDB2,99,Wed Aug 21 10:57:42 2024,NONE(no availabe)
dstage1,TestOra,3,Wed Aug 21 13:35:57 2024,Wed Aug 21 13:36:09 2024
dstage2,TestRou2,1,Fri May 10 17:01:58 2024,Fri May 10 17:01:59 2024
dstage2,runtimetest,OPEN ERROR

上のコマンドラインの引数の最後にプロジェクトが指定されていないので、全てのプロジェクト内の全てのジョブが対象になる。

 引数の最後にプロジェクトが指定されている場合は、指定されているプロジェクト内のジョブだけが対象になる。

-domainオプションでNONEを指定すると、dsjobと同じ様にDSエンジンに直接アクセスしに行くので、-user/-passwordで指定するユーザー、パスワードもdsjobと同じ様に指定する必要がある。
 
１行/ジョブで、以下の様に出力される。
`プロジェクト名,ジョブ名,ジョブのステータス,ジョブ開始時刻,前回の実行時間`
・最後の状態がジョブのコンパイルであれば、ステータスは99で出力されて、前回の実行時間はNONEになる。
・ジョブをコンパイルしていないと、OPEN ERRORになる。

 # 参考

InfoSphere DataStage Development Kit (Job Control Interfaces)
https://www.ibm.com/docs/en/iis/11.7?topic=reference-infosphere-datastage-development-kit-job-control-interfaces

Writing InfoSphere DataStage API Programs
https://www.ibm.com/docs/en/iis/11.7?topic=kit-writing-infosphere-datastage-api-programs

Error Codes
https://www.ibm.com/docs/en/iis/11.7?topic=kit-error-codes