ジョブネットとコントローラの自作
ジョブネットとコントローラを自作した。備忘録として載せておく。
インプットは下図。前提としてjob列の実行ファイルは作成済みであること。
prev は先行ジョブで、全ての先行ジョブが完了してからカレントジョブを実行する。
例えば、J04 の実行は J02, J03 の完了後に開始する。
status の値は 0(正常終了)、1(異常終了)、2(実行中)、9(未実行)を示す。
stime, etime はそれぞれ開始時刻、終了時刻を示す。
このインプットをCSVで保存する(jobnet.csv)。
◆ jobnet.csv
1job,status,stime,etime,prev1,prev2
2J01,9,,,Start,
3J02,9,,,J01,
4J03,9,,,J01,
5J04,9,,,J02,J03
6J05,9,,,J04,
7J06,9,,,J04,
8J07,9,,,J05,J06
9J08,9,,,J05,J06
10J09,9,,,J07,
11J10,9,,,J08,
12J11,9,,,J10,
13J12,9,,,J09,J11
◆jobnet.csv の更新の様子
下図は jobnet.csv 更新概念図。J01という親 job の中に複数の子 job がいる。
親 job の先頭と末尾で jobnet.csv を更新、エラー時も更新している。
親 job の先頭では jobnet.csv の status を2(実行中)に更新、開始日時 stime も現在日時で更新する。
親 job の末尾では status を0(正常終了)に更新、終了日時 etime も現在日時で更新する。
以下で示すプログラムを実行した時の、jobnet.csv が更新されていく様子。
◆各ジョブのサンプルコード(J01)
メイン処理の前後で jobnet.csv の stime, etime を更新している。ここではテストのため、子jobをただ一つの sleep だけ、さらにエラー判定も省略している。
1#!/bin/bash
2
3job=`echo $0 | sed -E 's/.+\/([^\/]+)$/\1/'`
4stime=`date +"%Y/%m/%d %H:%M:%S"`
5awk -v job=$job -v stime="$stime" 'BEGIN{FS=OFS=","}$1==job{$2=2;$3=stime}{print}' jobnet.csv 1<> jobnet.csv
6
7#メイン処理実行(スリープでテスト)
8sleep 3
9sts=$?
10
11etime=`date +"%Y/%m/%d %H:%M:%S"`
12awk -v job=$job -v etime="$etime" -v sts=$sts 'BEGIN{FS=OFS=","}$1==job{$2=sts;$4=etime}{print}' jobnet.csv 1<> jobnet.csv
13
14exit 0;
◆ bashプログラム(jobnet.sh)
二つの while ループがある。
一つ目は選択画面の表示と入力チェック、二つ目は jobnet.csv の監視とjob実行を行うメイン処理である。
job はバックグラウンドで実行する(並列稼働を可能とするため)
1#!/bin/bash
2
3if [ ! -r jobnet.csv ];then
4 echo "jobnet.csv がありません"
5 exit 1
6fi
7if [ ! -r jobnet.awk ];then
8 echo "jobnet.awk がありません"
9 exit 1
10fi
11
12while :
13do
14 echo "[1] ジョブネットの最初から実行する"
15 echo "[2] アベンドしたジョブ(1)や強制終了したジョブ(2)から再実行する"
16 echo "[9] 終了"
17 echo -n "番号を入力してEnterキーを押してください:"
18 read SELECT
19
20 if [ $SELECT = "1" ]; then
21 # jobnet.csvの初期化
22 awk 'BEGIN{FS=OFS=","}NR != 1{$2=9;$3=$4=""}{print}' jobnet.csv > jobnet.tmp
23 mv jobnet.tmp jobnet.csv
24 echo "ジョブネットを開始します。強制終了する場合は Ctrl+C を長押ししてください"
25 awk -F, '$5=="Start"{system("./"$1" &")}' jobnet.csv
26 STS=$?
27 if [ $STS != "0" ]; then
28 exit 1
29 fi
30 break
31 elif [ $SELECT = "2" ]; then
32 echo "ジョブネットを再開します。強制終了する場合は Ctrl+C を長押ししてください"
33 awk -F, '$2~/(1|2)/{system("./"$1" &")}' jobnet.csv
34 STS=$?
35 if [ $STS != "0" ]; then
36 exit 1
37 fi
38 break
39 elif [ $SELECT = "9" ]; then
40 exit 0
41 else
42 echo -n "正しい番号を入力してEnterキーを押してください:"
43 fi
44done
45
46while :
47do
48 sleep 1
49 # prevのジョブが全て完了しているかのチェック
50 awk -F, -f jobnet.awk jobnet.csv
51 STS=$?
52 # いずれかのjobで異常終了が発生していた場合、全体を異常終了する
53 if [ $STS = "1" ]; then
54 echo 'jobnet でアベンド発生'
55 exit 1
56 elif [ $STS = "0" ]; then
57 break
58 fi
59done
60
61echo "全てのジョブが正常終了しました"
62exit 0;
◆awkプログラム(jobnet.awk)
jobnet.csv の prev に書かれている job(先行ジョブ)の status をチェックして、正常終了したらカレント job を実行する。
job はバックグラウンドで実行する(並列稼働を可能とするため)
1NR == 1{next} # 一行目はヘッダーなのでスキップ
2$5 == "Start"{next} # Startのジョブはシェルで実行済みなのでスキップ
3{
4 # prev配列の初期化
5 delete prev
6 # statusが2(実行中)または9(未実行)の場合、終了ステータスを2(実行中)とする
7 if($2 == 2 || $2 == 9) exitSts = 2
8 # prevは4番目以降のフィールド
9 for(i=5;i<=NF;i++){
10 # prevが未設定の場合はループを抜ける
11 if(!$i)break
12 # prevのjobのstatusを取得する
13 com = "awk -F, -v prev="$i" '$1==prev{print $2;exit}' jobnet.csv"
14 com | getline status
15 close(com)
16 # 取得したstatusをprev配列にセット
17 prev[$i] = status
18 }
19 # prev配列に1(異常終了)が含まれていた場合、直ちに全体を異常終了する
20 for(status in prev){
21 if(prev[status] == 1)exit(1)
22 }
23 # prev配列に2(実行中)または9(未実行)が含まれていた場合、カレントjobの処理をスキップ
24 for(status in prev){
25 if(prev[status] == 2 || prev[status] == 9) next
26 }
27 # prevが全て正常終了していて、かつ、カレントjobのstatusが9(未実行)の場合は、実行開始する
28 if($2 == 9)system("./"$1" &")
29}
30END{exit(exitSts)}