REST APIを使ってコマンドジョブをコードで管理する

作成日 2026/08/24

はじめに

今回は、Hinemos ver.7.2 の REST API を使って、コマンドジョブの登録・更新をコードだけで行う方法についてご紹介いたします。


Hinemos では GUI からジョブを設定するのが一般的ですが、同じ構成のジョブを大量に登録したり、
コマンドパスを一括で変更したりする場合は、 GUI 操作では手間がかかります。
REST API を活用することで、こういった作業をスクリプトで自動化できます。

参考ドキュメント:Hinemos ver.7.2 REST API 開発ガイド(ja_RestAPI_7.2_rev1.html) 第18章「ジョブ管理」
※ REST API 開発ガイドはHinemosのサブスクリプション契約が必要なドキュメントです。


それでは、実際に試してみましょう。

環境情報

今回は以下の環境で検証を行いました。

・利用環境:

Linux版 Hinemosマネージャ、Hinemos Webクライアント(ver.7.2)

Hinemosエージェントサーバ(ファシリティID: agent

・言語:Python(/usr/libexec/platform-python、RHEL 8)

・対象ジョブユニット:JOBUNIT

・登録するコマンドジョブ:

ジョブID ジョブ名 起動コマンド
CMD_JOB_01 コマンドジョブ01 /opt/scripts/job_01.sh
CMD_JOB_02 コマンドジョブ02 /opt/scripts/job_02.sh
CMD_JOB_03 コマンドジョブ03 /opt/scripts/job_03.sh
CMD_JOB_04 コマンドジョブ04 /opt/scripts/job_04.sh
CMD_JOB_05 コマンドジョブ05 /opt/scripts/job_05.sh

事前準備:GUI でジョブユニットを作成する

REST API でコマンドジョブを登録するには、ジョブユニットがあらかじめ作成済みであることが前提となります。

今回は「ジョブユニットだけ GUI で作成し、その配下のコマンドジョブは API で登録する」という方針で進めます。

Hinemos Webクライアントを起動し、ジョブ設定パースペクティブからジョブユニット「JOBUNIT」を新規作成・登録してください。
この時点ではコマンドジョブは何も登録しなくて構いません。

空のJOBUNITが登録された状態のジョブツリー

認証トークンの取得

Hinemos の REST API は Bearer トークン認証を使用します。
(参照:REST API 開発ガイド「2.1 ログイン」)


まず、以下のスクリプトを実行してトークンを取得します。引数には「ログインユーザ名 / ログインパスワード」を指定します。

python getToken.py hinemos hinemos

実行すると./token_hinemos.txtにトークンが保存されます。以降のスクリプトはこのファイルを読み込んで認証に使用します。

getToken.py のコード

#!/usr/libexec/platform-python
from urllib.request import *
import sys
import json

args = sys.argv
user = args[1]
pas  = args[2]

req = Request(
    url='http://[HinemosマネージャのIPアドレス]:8080/HinemosWeb/api/AccessRestEndpoints/access/login',
    data=json.dumps({'userId': user, 'password': pas}).encode(),
    headers={'Content-Type': 'application/json'},
    method='POST')

with urlopen(req) as res:
    body = json.loads(res.read())
    print(body)

try:
    file = open("./token_" + user + ".txt", 'w')
    file.write(body['token']['tokenId'])
except Exception as e:
    print(e)
finally:
    file.close()

コマンドジョブの一括登録

REST API 開発ガイドの「18.2 コマンドジョブを追加する」に記載されている
POST /jobunit/{jobunitId}/commandJobエンドポイントを使用します。

このエンドポイントを使うにあたり、Hinemos ではジョブ設定の変更操作の前にロック(編集セッション)の取得が必要です。
処理の流れは以下の通りです。


1.ジョブツリー取得 API でロック取得に必要な情報を確認する

2.ロックを取得する

3.コマンドジョブを 1 件ずつ登録する(今回は 5 件)

4.ロックを解放する


以下のスクリプトを実行してコマンドジョブを一括登録します。引数にはログインユーザ名を指定します。

python registerJobUnit.py hinemos

registerJobUnit.py のコード

#!/usr/libexec/platform-python
# 前提: GUIで JOBUNIT(空のジョブユニット)を作成・登録済みであること
from urllib.request import *
from urllib.error import HTTPError
import sys
import json

args = sys.argv
user = args[1]

JOBUNIT_ID  = "JOBUNIT"
PARENT_ID   = "JOBUNIT"
FACILITY_ID = "agent"

COMMANDS = [
    ("CMD_JOB_01", "コマンドジョブ01", "/opt/scripts/job_01.sh"),
    ("CMD_JOB_02", "コマンドジョブ02", "/opt/scripts/job_02.sh"),
    ("CMD_JOB_03", "コマンドジョブ03", "/opt/scripts/job_03.sh"),
    ("CMD_JOB_04", "コマンドジョブ04", "/opt/scripts/job_04.sh"),
    ("CMD_JOB_05", "コマンドジョブ05", "/opt/scripts/job_05.sh"),
]

managerUrl = "http://[HinemosマネージャのIPアドレス]:8080/HinemosWeb/api/"

try:
    f = open("./token_" + user + ".txt")
    token = f.read()
finally:
    f.close()

def api(method, path, body=None):
    headers = {"Authorization": "Bearer " + token}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    req = Request(url=managerUrl + path, headers=headers, data=data, method=method)
    with urlopen(req) as res:
        raw = res.read()
        return json.loads(raw) if raw else None

def wait_rule():
    return {
        "suspend": False, "skip": False,
        "skipEndStatus": "NORMAL", "skipEndValue": 0,
        "condition": "AND", "objectGroup": [],
        "endCondition": False, "endStatus": "NORMAL", "endValue": 0,
        "exclusiveBranch": False,
        "exclusiveBranchEndStatus": "NORMAL", "exclusiveBranchEndValue": 0,
        "exclusiveBranchNextJobOrderList": [],
        "calendar": False, "calendarEndStatus": "NORMAL", "calendarEndValue": 0,
        "jobRetryFlg": False, "jobRetryEndStatus": "NORMAL",
        "jobRetry": 1,
        "jobRetryInterval": 0,
        "startDelay": False, "startDelaySession": False, "startDelaySessionValue": 1,
        "startDelayTime": False, "startDelayConditionType": "AND",
        "startDelayNotify": False, "startDelayNotifyPriority": "CRITICAL",
        "startDelayOperation": False, "startDelayOperationType": "STOP_AT_ONCE",
        "startDelayOperationEndStatus": "NORMAL", "startDelayOperationEndValue": 0,
        "endDelay": False, "endDelaySession": False, "endDelaySessionValue": 1,
        "endDelayJob": False, "endDelayJobValue": 1,
        "endDelayTime": False, "endDelayConditionType": "AND",
        "endDelayNotify": False, "endDelayNotifyPriority": "CRITICAL",
        "endDelayOperation": False, "endDelayOperationType": "STOP_AT_ONCE",
        "endDelayOperationEndStatus": "NORMAL", "endDelayOperationEndValue": 0,
        "endDelayChangeMount": False, "endDelayChangeMountValue": 0.0,
        "multiplicityNotify": False, "multiplicityNotifyPriority": "CRITICAL",
        "multiplicityOperation": "WAIT", "multiplicityEndValue": 0,
        "queueFlg": False
    }

def end_status():
    return [
        {"type": "NORMAL",   "value": 0,  "startRangeValue": 0,  "endRangeValue": 0},
        {"type": "WARNING",  "value": 1,  "startRangeValue": 1,  "endRangeValue": 1},
        {"type": "ABNORMAL", "value": -1, "startRangeValue": -1, "endRangeValue": -1}
    ]

def cmd_job_body(job_id, name, start_cmd):
    return {
        "id": job_id,
        "parentId": PARENT_ID,
        "name": name,
        "waitRule": wait_rule(),
        "endStatus": end_status(),
        "beginPriority": "NONE",
        "normalPriority": "NONE",
        "warnPriority": "NONE",
        "abnormalPriority": "NONE",
        "notifyRelationInfos": [],
        "command": {
            "facilityID": FACILITY_ID,
            "processingMethod": "ALL_NODE",
            "managerDistribution": False,
            "startCommand": start_cmd,
            "stopType": "DESTROY_PROCESS",
            "stopCommand": "",
            "specifyUser": False,
            "messageRetry": 3,
            "messageRetryEndFlg": False,
            "messageRetryEndValue": 1,
            "commandRetryFlg": False,
            "commandRetry": 1,
            "commandRetryEndStatus": "NORMAL",
            "normalJobOutputInfo": {
                "sameNormalFlg": True, "directory": "/", "fileName": "/",
                "appendFlg": False, "failureOperationFlg": False,
                "failureOperationType": "STOP_SUSPEND",
                "failureOperationEndStatus": "NORMAL", "failureOperationEndValue": 0,
                "failureNotifyFlg": False, "failureNotifyPriority": "CRITICAL",
                "valid": False
            },
            "errorJobOutputInfo": {
                "sameNormalFlg": True, "directory": "/", "fileName": "/",
                "appendFlg": False, "failureOperationFlg": False,
                "failureOperationType": "STOP_SUSPEND",
                "failureOperationEndStatus": "NORMAL", "failureOperationEndValue": 0,
                "failureNotifyFlg": False, "failureNotifyPriority": "CRITICAL",
                "valid": False
            },
            "jobCommandParamList": [],
            "envVariable": []
        }
    }

# ── Step 1: updateTime 取得 ──────────────────────────────────────────────
print("==[Step 1] GET job tree → updateTime ==")
tree = api("GET", "JobRestEndpoints/job/setting/job_treeFull")
updateTime = None
def find_update_time(items):
    global updateTime
    for item in items:
        d = item.get("data", {})
        if d.get("type") == "JOBUNIT" and d.get("id") == JOBUNIT_ID:
            updateTime = d.get("updateTime")
        if item.get("children"):
            find_update_time(item["children"])
find_update_time(tree if isinstance(tree, list) else [tree])
if updateTime is None:
    print("FAILED: " + JOBUNIT_ID + " not found in job tree")
    sys.exit(1)
print("updateTime=" + str(updateTime))

# ── Step 2: ロック取得 ────────────────────────────────────────────────────
print("==[Step 2] Lock ==")
lock = api("POST",
           "JobRestEndpoints/job/setting/jobunit/" + JOBUNIT_ID + "/lock",
           {"updateTime": updateTime, "forceFlag": True})
session = lock["editSession"]
print("session=" + str(session))

# ── Step 3: POST /commandJob でコマンドジョブを1件ずつ登録 ────────────────
print("==[Step 3] POST: register command jobs ==")
for cmd in COMMANDS:
    job_id, name, start_cmd = cmd
    try:
        api("POST",
            "JobRestEndpoints/job/setting/jobunit/" + JOBUNIT_ID + "/commandJob",
            cmd_job_body(job_id, name, start_cmd))
        print("  OK: " + job_id)
    except HTTPError as e:
        msg = json.loads(e.read().decode()).get("message", "ERROR")
        print("  FAILED [" + job_id + "]: " + msg)

# ── Step 4: ロック解放 ────────────────────────────────────────────────────
print("==[Step 4] Unlock ==")
try:
    req = Request(
        url=managerUrl + "JobRestEndpoints/job/setting/jobunit/" + JOBUNIT_ID + "/lock/" + str(session),
        headers={"Authorization": "Bearer " + token},
        method="DELETE"
    )
    with urlopen(req) as res:
        res.read()
    print("OK")
except HTTPError as e:
    print("FAILED: " + json.loads(e.read().decode()).get("message", "ERROR"))

スクリプトを実行すると、以下のように出力されます。

==[Step 1] GET job tree → updateTime ==
updateTime=1746xxxxxxxxx
==[Step 2] Lock ==
session=12345
==[Step 3] POST: register command jobs ==
  OK: CMD_JOB_01
  OK: CMD_JOB_02
  OK: CMD_JOB_03
  OK: CMD_JOB_04
  OK: CMD_JOB_05
==[Step 4] Unlock ==
OK

GUI での登録確認

スクリプト実行後、Hinemos Webクライアントのジョブ設定パースペクティブを確認します。
JOBUNIT配下に 5 件のコマンドジョブが登録されていることが確認できます。

JOBUNIT配下にCMD_JOB_01_05 が並んだジョブツリー

登録されたジョブをダブルクリックして開き、起動コマンドが正しく設定されていることも確認します。

登録したCMD_JOB_01のコマンドタブ

起動コマンドの一括更新

登録済みのコマンドジョブの起動コマンドを変更するには、REST API 開発ガイドの
「18.4 コマンドジョブを変更する」に記載のPUT /jobunit/{jobunitId}/commandJob/{jobId}を使用します。


以下のスクリプトは引数で指定したジョブの起動コマンドを変更します。
引数は「ログインユーザ名 / ジョブユニットID / ジョブID / 新しい起動コマンド」の順です。

python updateCommandJob.py hinemos JOBUNIT CMD_JOB_01 /opt/new/scripts/job_01.sh

updateCommandJob.py のコード

#!/usr/libexec/platform-python
from urllib.request import *
from urllib.error import HTTPError
import sys
import json

args        = sys.argv
user        = args[1]
jobunitId   = args[2]
jobId       = args[3]
newStartCmd = args[4]

managerUrl = "http://[HinemosマネージャのIPアドレス]:8080/HinemosWeb/api/"

try:
    file = open("./token_" + user + ".txt")
    token = file.read()
finally:
    file.close()

def api(method, path, body=None):
    headers = {"Authorization": "Bearer " + token}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    req = Request(url=managerUrl + path, headers=headers, data=data, method=method)
    with urlopen(req) as res:
        raw = res.read()
        return json.loads(raw) if raw else None

# ジョブツリーから対象ジョブのデータを取得
tree = api("GET", "JobRestEndpoints/job/setting/job_treeFull")

updateTime = None
jobData    = None

def find_in_tree(items):
    global updateTime, jobData
    for item in items:
        d = item.get("data", {})
        if d.get("type") == "JOBUNIT" and d.get("id") == jobunitId:
            updateTime = d.get("updateTime")
        if d.get("id") == jobId and d.get("jobunitId") == jobunitId:
            jobData = d
        if item.get("children"):
            find_in_tree(item["children"])

find_in_tree(tree if isinstance(tree, list) else [tree])

if updateTime is None:
    print("ERROR: jobunit not found: " + jobunitId)
    sys.exit(1)
if jobData is None:
    print("ERROR: job not found: " + jobId)
    sys.exit(1)

# ロック取得
lock = api("POST",
           "JobRestEndpoints/job/setting/jobunit/" + jobunitId + "/lock",
           {"updateTime": updateTime, "forceFlag": True})
session = lock["editSession"]
print("Locked. session=" + str(session))

# startCommand を変更して PUT
jobData["command"]["startCommand"] = newStartCmd
try:
    api("PUT",
        "JobRestEndpoints/job/setting/jobunit/" + jobunitId + "/commandJob/" + jobId + "?isClient=true",
        jobData)
    print("Updated: " + jobId + "  startCommand=" + newStartCmd)
except HTTPError as e:
    print("PUT failed: " + json.loads(e.read().decode()).get("message", "ERROR"))

# ロック解放
try:
    req = Request(
        url=managerUrl + "JobRestEndpoints/job/setting/jobunit/" + jobunitId + "/lock/" + str(session),
        headers={"Authorization": "Bearer " + token},
        method="DELETE"
    )
    with urlopen(req) as res:
        res.read()
    print("Unlocked.")
except HTTPError as e:
    print("Unlock failed: " + json.loads(e.read().decode()).get("message", "ERROR"))

5 件まとめてパスを変更する場合は、シェルのループで実行できます。

for i in 01 02 03 04 05; do
  python updateCommandJob.py hinemos JOBUNIT CMD_JOB_$i \
    /opt/new/scripts/job_$i.sh
done

実行結果:

Locked. session=12346
Updated: CMD_JOB_01  startCommand=/opt/new/scripts/job_01.sh
Unlocked.
Locked. session=12347
Updated: CMD_JOB_02  startCommand=/opt/new/scripts/job_02.sh
Unlocked.
...

GUI での更新確認

スクリプト実行後、GUI でジョブを開き、起動コマンドが更新されていることを確認します。

更新したCMD_JOB_01のコマンドタブ

リクエストボディ作成時のポイント

REST API でコマンドジョブを登録・更新する際、リクエストボディを組み立てる上で確認が必要だったポイントをご紹介します。

その1:stopTypeとstopCommandの組み合わせ

REST API 開発ガイド「18.2 コマンドジョブを追加する」のstopTypeパラメータについて、
EXECUTE_COMMAND(停止コマンド実行)を指定する場合はstopCommandも合わせて設定する必要があります。
停止コマンドを使用しない場合はDESTROY_PROCESS(プロセス終了)を指定してください。

# 停止コマンドを使用しない場合
"stopType": "DESTROY_PROCESS",
"stopCommand": "",

その2:通知優先度の 4 フィールドは必須

REST API 開発ガイド「18.2 コマンドジョブを追加する」のリクエストボディには、通知優先度として以下の4フィールドの指定が必要です。
通知を使用しない場合は "NONE"を設定してください。

"beginPriority":    "NONE",
"normalPriority":   "NONE",
"warnPriority":     "NONE",
"abnormalPriority": "NONE",

その3:リトライ系フィールドの下限値

リトライ機能を使用しない場合でも、以下のフィールドには0ではなく、1以上の値を設定してください。

フィールド 下限値
commandRetry(繰り返し実行回数) 1
jobRetry(繰り返し実行回数) 1
messageRetryEndValue(エラー時終了値) 1

その4:出力情報のdirectoryフィールド

ファイル出力を使用しない場合(valid: false)でも、normalJobOutputInfo / errorJobOutputInfoのdirectoryおよびfileNameフィールドには
空文字以外の値を設定してください。

"normalJobOutputInfo": {"directory": "/", "fileName": "/", "valid": False, ...}

その5:遅延設定フィールドの下限値

開始遅延・終了遅延の機能を使用しない場合でも、以下のフィールドには0ではなく、1以上の値を設定してください。
0を設定すると、GUI でジョブを開いた際に入力エラーが表示されます。

フィールド 下限値
startDelaySessionValue 1
endDelaySessionValue 1
endDelayJobValue 1

おわりに

今回は、Hinemos ver.7.2 の REST API を使ってコマンドジョブの登録・更新をコードで行う方法をご紹介しました。

REST API 開発ガイドに記載の各エンドポイントを活用することで、大量のジョブ登録やコマンドパスの一括変更といった作業を
スクリプトで自動化できます。
リクエストボディを組み立てる際は、第6章でご紹介したポイントを参考にしていただければ幸いです。

大量のジョブをコードで管理したい場合や、設定を一括変更したい場合にご活用ください。

  • 最短3分で完了

    トライアル on AWS

    お客様のAWS環境でHinemosをお試し

  • まずはお気軽に

    導入のご相談

    Hinemos導入に関する
    各種ご相談・お見積り