OCFリソースエージェント開発者ガイド
著者
  • Florian Haas (hastexo)

  • John Shi (SUSE): ocft README 原文

  • Dejan Muhamedagic (SUSE): ocft ドキュメント見直し

著作権表示
ライセンス情報

この文書に含まれる文章、図は、クリエイティブ・コモンズ表示 - 継承 3.0 非移植 (CC BY-SA 3.0) によってライセンスされています。

日本語訳について

1. はじめに

本書は、OCF(Open Cluster Framework)準拠のクラスタリソースエージェントに関連するすべての開発者、メンテナおよびコントリビュータのためのガイドとリファレンスです。本書は、リソースエージェントの構造や一般的な機能の説明と、リソースエージェントAPIの説明、リソースエージェント作成者に役に立つヒントやヘルプを提供します。

1.1. リソースエージェントとは何か?

リソースエージェントとは、クラスタリソースを管理する実行ファイルです。クラスタリソースとは、「クラスタが管理するものはすべてリソースである」という以外に正式な定義はありません。いくつか例を挙げると、IPアドレス、ファイルシステム、データベースサービス、仮想マシン全体など多種多様なものがクラスタリソースになりえます。

1.2. 誰がまたは何がリソースエージェントを使うのか?

OCF(Open Cluster Framework)に準拠したクラスタ管理アプリケーションが、本書で説明されているリソースエージェントを使ってリソースを管理することができます。本書の執筆時点では、Linuxプラットフォーム用に2つのOCF準拠クラスタ管理アプリケーションがあります。

  • Pacemaker : CorosyncおよびHeartbeatクラスタメッセージングフレームワークの両方をサポートするクラスタマネージャ。Pacemakerは、Linux-HAプロジェクトから進化し独立しています。

  • RGmanager : Red Hat Cluster Suiteでバンドルされているクラスタマネージャ。これは、Corosyncクラスタメッセージングフレームワークだけをサポートします。

1.3. リソースエージェントはどの言語で記述するのか?

OCF準拠リソースエージェントは どのような プログラミング言語ででも実装可能です。APIは言語を問いません。しかし、ほとんどのリソースエージェントはシェルスクリプトとして実装されています。本ガイドは、主にシェル言語で記述されたサンプルコードを使います。

1.4. 名前付け規約はありますか?

あります! リソースエージェントの名称は次の規約に従うという合意があります: リソースエージェントの名前は小文字とし、単語の区切りはダッシュ文字とすること(例: example-agent-name)。

既存のエージェントの中にはこの規約に従っているものもそうでないものもありますが、今後作成するエージェントは必ずこのルールに従ったものにするという意図です。

2. API定義

2.1. 環境変数

リソースエージェントは、管理するリソースの設定情報を全て環境変数を通して受け取ります。これらの環境変数の名前は必ず、リソースのパラメタ名に OCF_RESKEY_ という接頭語を付加したものになります。たとえば、リソースに 192.168.1.1 という値が設定された ip というパラメタがある場合、リソースエージェントはこの値を保持している OCF_RESKEY_ip という環境変数にアクセスすることができます。

ユーザによる設定が必須ではないリソースパラメータ(すなわち、リソースエージェントメタデータのパラメータ定義で required="true" が指定されていないパラメータ)に対しては、リソースエージェントは以下を行う必要があります。

  • 適切なデフォルト値を提供すること。これはメタデータの中に明記すべきです。慣例として、リソースエージェントでは、OCF_RESKEY_<parametername>_default と名付けられた変数にこのデフォルトを保持するようにします。

  • もしくは、未設定の場合に正しく対処すること。

さらに、クラスタマネージャは meta リソースパラメータも使用することがあります。これらはリソース設定が直接適用されるものではなく、クラスタリソースマネージャがリソースを どのように 管理したいかを指定するものです。たとえば、Pacemakerクラスタマネージャは target-role metaパラメータを使って、リソースが起動されるべきか停止されるべきかを指定します。

metaパラメータは OCF_RESKEY_CRM_meta_ 名前空間でリソースエージェントに渡されます(この場合、ハイフンはアンダースコアに変換されます)。したがって、 target-role 属性は OCF_RESKEY_CRM_meta_target_role と名付けられた環境変数にマッピングされます。

その他のシステム環境変数についてはスクリプト変数の項に記載しています。

2.2. アクション

全てのリソースエージェントは必ず1つのコマンドライン引数を取ります。これにはリソースエージェントが実行しようとしているアクションを指定します。以下のアクションは全てのリソースエージェントがサポートしなければなりません。

  • start — リソースを起動します。

  • stop — リソースを停止します。

  • monitor — リソースの状態を問合せします。

  • meta-data — リソースエージェントメタデータをダンプします。

これに加えて、リソースエージェントは以下のアクションをオプションとしてサポートすることもあります。

  • promote — リソースの役割を Master に変更します(Master/Slaveリソースのみ)。

  • demote — リソースの役割を Slave に変更します(Master/Slaveリソースのみ)。

  • migrate_to および migrate_from — リソースのライブマイグレーションを実施します。

  • validate-all --リソースの設定を検証します。

  • usage or help — リソースエージェントがクラスタマネージャではなくコマンドラインから起動された場合のため、使用方法を表示します。

  • notify — 他のクローンリソースの状態変化をリソースに通知します。

  • status —  monitor の歴史的な(非推奨の)別名。

Note
訳注: 上記以外にも reload というアクションが存在しますが、原文でも記載していないため本ドキュメントでは説明しません。

2.3. タイムアウト

アクションのタイムアウトの処理は、リソースエージェントの範囲の外で実施されます。リソースエージェントアクションがどれぐらいの時間実行されているかを監視し、もし所定の時間内にアクションが終了しない場合にそのアクションを停止するのは、クラスタマネージャの責任です。したがって、リソースエージェント自身は、タイムアウトをチェックする必要はありません。

しかし、実際的なタイムアウト値をリソースエージェントからユーザに 推奨 することはできます(正しく設定すれば、クラスタマネージャによって適切に実行される)。リソースエージェントから、その提案タイムアウト値をどのように提示するかについては以下のセクションを参照してください。

2.4. メタデータ

全てのリソースエージェントは、その目的やサポートしているパラメータを一連の XML メタデータとして記述しなければなりません。このメタデータは、クラスタ管理アプリケーションでオンラインヘルプとして利用されたり、リソースエージェントのmanページをこれから生成したりします。以下は、架空のリソースエージェントにおける一連の架空のメタデータです。

<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="foobar">
  <version>0.1</version>
  <longdesc lang="en">
This is a fictitious example resource agent written for the
OCF Resource Agent Developers Guide.
  </longdesc>
  <shortdesc lang="en">Example resource agent
  for budding OCF RA developers</shortdesc>
  <parameters>
    <parameter name="eggs" unique="0" required="1">
      <longdesc lang="en">
      Number of eggs, an example numeric parameter
      </longdesc>
      <shortdesc lang="en">Number of eggs</shortdesc>
      <content type="integer"/>
    </parameter>
    <parameter name="superfrobnicate" unique="0" required="0">
      <longdesc lang="en">
      Enable superfrobnication, an example boolean parameter
      </longdesc>
      <shortdesc lang="en">Enable superfrobnication</shortdesc>
      <content type="boolean" default="false"/>
    </parameter>
    <parameter name="datadir" unique="0" required="1">
      <longdesc lang="en">
      Data directory, an example string parameter
      </longdesc>
      <shortdesc lang="en">Data directory</shortdesc>
      <content type="string"/>
    </parameter>
  </parameters>
  <actions>
    <action name="start"        timeout="20" />
    <action name="stop"         timeout="20" />
    <action name="monitor"      timeout="20"
                                interval="10" depth="0" />
    <action name="notify"       timeout="20" />
    <action name="reload"       timeout="20" />
    <action name="migrate_to"   timeout="20" />
    <action name="migrate_from" timeout="20" />
    <action name="meta-data"    timeout="5" />
    <action name="validate-all"   timeout="20" />
  </actions>
</resource-agent>

resource-agent 要素はリソースエージェント毎に必ず1つ記述する必要があり、リソースエージェントの nameversion を定義します。

resource-agentlongdescshortdesc 要素は、リソースエージェントの機能について長い説明文と短い説明文を記述します。 shortdesc は、そのリソースエージェントが何をするものなのかの一行説明文で、通常簡易な一覧表などで使われます。それに対して longdesc は、リソースエージェントについてできるだけ詳細に説明します。

parameters 要素はリソースエージェントのパラメータについて説明するもので、子供として parameter 要素をリソースエージェントがサポートするパラメータの数だけ持ちます。

それぞれの parameter は、resource-agent 本体と同様に shortdesclongdesc の記述があり、さらにそのパラメータに期待される内容を説明する content 要素が続きます。

content 要素には、4種類の属性が存在します。

  • type はパラメータタイプ( string , integer , boolean )を設定します。デフォルト: string

  • required はパラメータ設定が必須( required="true" )であるかオプション( required="false" )であるかを設定します。

  • オプションパラメータに関しては、default 属性によって適切なデフォルト値を提供することが慣例になっています。

  • 最後に、 unique 属性(許容値: truefalse )は、この特定のリソースタイプのこのパラメータに対してその指定した値がクラスタ全体で一意となる必要があることを示します。たとえば、高可用フローティングIPアドレスは unique として宣言されています。それは一つのIPアドレスはクラスタ全体で一箇所でのみ実行すべきものであり、重複を避けるためです。

actions のリストは、リソースエージェントがサポートしているアクションを定義します。

それぞれの action は、それぞれ固有の timeout 値を記載します。これは、アクションに対して設定すべき 最小限の タイムアウトはいくつか、というヒントをユーザに示しています。これは、ある種のリソース(例: IPアドレスやファイルシステム) では短時間で起動/停止できたり、別のリソース(例: データベース) は数分かかったりする、ということに対応するためです。

さらに、繰り返しアクション(例: monitor )は、推奨の最小実行間隔 interval も指定する必要があります。 interval は、同じアクションの連続した2回の実行の間隔を指定します。 timeout と同じく、この値のデフォルト設定はできません。つまり、最小実行間隔をどのように設定すればいよいかというヒントをユーザに示しているにすぎません。

3. 戻り値

リソースエージェントは、アクションの実行に対して必ず定義された戻り値で終了し、アクションの実行結果を呼び出し元に通知しなければなりません。以下の項では戻り値について詳細に説明します。

3.1. OCF_SUCCESS (0)

アクションは正しく終了しました。これは次の各アクション start, stop, promote, demote, migrate_from, migrate_to, meta_data, help, usage が成功したときに対する戻り値です。

しかし、monitor (および非推奨の別名 status)に対しては少し異なる慣習が適用されます。

  • primitive (stateless)リソースに関しては、 monitor からの OCF_SUCCESS は、リソースが実行されていることを意味します。非実行中で正常停止しているリソースに対しては OCF_NOT_RUNNING を返す必要があります。

  • master/slave (stateful)リソースに関しては、 monitor からの OCF_SUCCESS は、リソースが Slaveモードで 実行されていることを意味します。Masterモードで実行されているリソースは OCF_RUNNING_MASTER を、正常停止しているリソースは OCF_NOT_RUNNING を返す必要があります。

3.2. OCF_ERR_GENERIC (1)

アクションは汎用エラーを返しました。リソースエージェントは、以下の項で定義している特定のエラーコードでは問題を正確に表せない場合にのみ、この終了コードを使用すべきです。

クラスタリソースマネージャは、この終了コードを ソフト エラーとして解釈します。この場合、特に設定されていない限り、リソースマネージャは OCF_ERR_GENERIC で失敗したところと同じ場所でリカバリしようと試みます。つまり通常は同じノードでリソースが再起動されます。

3.3. OCF_ERR_ARGS (2)

リソースの設定がこのマシン上では不正です。例えば、ノード上には存在しないパスを参照している場合などです。

Note
リソースエージェントは、サポートしていないアクションを実行するよう指示された場合、このエラーを返すべきではありません。その代わり、そのような状況では、OCF_ERR_UNIMPLEMENTEDを返すべきです。

3.4. OCF_ERR_UNIMPLEMENTED (3)

リソースエージェントは、エージェントに実装されていないアクションを実行するよう指示されました。

リソースエージェントのアクションは全てが必須なわけではありません。promote, demote, migrate_to, migrate_from, notify は全て省略可能なアクションであり、リソースエージェントが実装している場合もしていない場合もあります。たとえば、statefulではないリソースを誤って master/slave リソースとして設定してしまった場合、リソースエージェントは promote および demote アクションから OCF_ERR_UNIMPLEMENTED を返却して、ユーザに設定ミスを警告すべきです。

3.5. OCF_ERR_PERM (4)

アクションは、不十分なアクセス権限により失敗しました。これは、エージェントがあるファイルを開くことができなかった、指定されたソケットでlistenできなかった、ディレクトリに書き込みができなかった、あるいはそれに類する原因によるものです。

クライアントリソースマネージャは、この終了コードを ハード エラーとして解釈します。この場合、特に設定されていないかぎり、リソースマネージャは別のノード(アクセス権限に関する問題がないと思われるノード)でリソースを再起動することにより、このエラーで故障したリソースをリカバリしようとします。

3.6. OCF_ERR_INSTALLED (5)

アクションは、それが実行されたノードに必要コンポーネントがなかったために失敗しました。これには必要なバイナリが実行可能ではなかった、あるいは重要な設定ファイルが読み込めなかった、などがありえます。

クラスタマネージャは、この終了コードを ハード エラーとして解釈します。この場合、特に設定されていないかぎり、リソースマネージャは別のノード(必要ファイルやバイナリがあると思われるノード)でリソースを再起動することによって、このエラーで故障したリソースをリカバリしようとします。

3.7. OCF_ERR_CONFIGURED (6)

アクションは、ユーザがリソースの設定を誤っていたため失敗しました。たとえば、ユーザが、本来整数を設定すべきパラメータに英数字文字列を設定したような場合です。

クラスタリソースマネージャは、この終了コードを 致命的 エラーとして解釈します。この設定エラーはクラスタ全体で発生するものであるため、同じノードではもちろんのこと、異なるノードでそのようなリソースをリカバリするのは意味がありません。リソースがこのエラーで故障すると、クラスタマネージャはリソースを停止しようとし、管理者の介入を待ちます。

3.8. OCF_NOT_RUNNING (7)

リソースは実行されていないと判断しました。これは、 monitor アクションのみによって返される終了コードです。これにはリソースが 正常に 終了した場合も、そもそも起動されていなかった場合も含みます。

リソースがエラー状態のせいで実行されていない場合、 monitor アクションは、代わりに OCF_ERR_ 終了コードの一つか、 OCF_FAILED_MASTER を返すべきです。

3.9. OCF_RUNNING_MASTER (8)

リソースは、 Master roleで実行されていると判断しました。これは stateful (Master/Slave)リソースにのみ、そして、その monitor アクションにのみ適用されます。

注意点として、「slaveモードで実行中である」ということを示す特定の終了コードはありません。これは正常実行中の primitive リソースと slave として実行している stateful リソースの間には機能的な違いは何も無いからです。Slave role として正常実行中の stateful リソースは、 monitor アクションに対しては単に OCF_SUCCESS を返すだけで良いです。

3.10. OCF_FAILED_MASTER (9)

リソースは Master roleで故障していると判断しました。これは stateful (Master/Slave)リソースにのみ、そして、その monitor アクションにのみ適用されます。

クラスタリソースマネージャは、この終了コードを_ソフト_ エラーとして解釈します。この場合、特に設定されていない限り、リソースマネージャは $OCF_FAILED_MASTER で失敗したところと同じ場所でリカバリしようと試みます。つまり通常は同じノードでリソースの降格、停止、起動、昇格が行われます。

4. リソースエージェントの構造

典型的な(シェルベースの)リソースエージェントは、本項で列挙した順序に従った標準的な構成を持ちます。本項では foobar という名前の架空のリソースエージェントを用いて、実装すべきさまざまなアクションに対してリソースエージェントに期待する動作を記述します。

4.1. リソースエージェントインタプリタ

スクリプトとして実装されたリソースは、標準の"shebang" (#!)ヘッダ構文を使い、そのインタプリタを指定しなければなりません。

#!/bin/sh

リソースエージェントをシェルで記述する場合、一般的には標準シェルインタプリタ(#!/bin/sh)を指定することが好まれますが、これは必須ではありません。 /bin/sh 互換であると宣言したリソースエージェントでは、特定のシェル固有の構文(たとえば bash 固有の ${!variable} 構文など)を使用してはいけません。そのようなリソースエージェントに対しては checkbashisms などの清書用ユーティリティを必要に応じて実行することが望ましいです。

既存の sh 互換のリソースエージェントを、 bashksh 、その他一般的ではないシェルでしか実行できなくしてしまうパッチはレグレッションバグであるとみなされます。しかし新しいリソースエージェントにおいて、特定のシェル、たとえば /bin/bash をインタプリタとして明確に指定することは全く問題ありません。

4.2. 著者およびライセンス情報

リソースエージェントは、リソースエージェントの著者や著作権保持者、そして、リソースエージェントに適用されるライセンスを記載したコメントを記述する必要があります。

#
#   Resource Agent for managing foobar resources.
#
#   License:      GNU General Public License (GPL)
#   (c) 2008-2010 John Doe, Jane Roe,
#                 and Linux-HA contributors

リソースエージェントが、複数のバージョンが存在するライセンスに当てはまる場合、最新のバージョンが適用されるものとします。

4.3. 初期化

すべてのシェルリソースエージェントは ocf-shellfuncs 関数ライブラリを読み込むようにします。以下の構文のように $OCF_FUNCTIONS_DIR を通して行います。これはテスト目的やドキュメント生成のためにコマンドラインから上書きできるようにするためです。

# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs

4.4. リソースエージェントのアクションを実装する関数

次に続けて、リソースエージェントが提供しているアクションを実装する関数を記述します。個別のアクションの詳細についてはリソースエージェントのアクションで説明します。

4.5. 実行ブロック

ここは、リソースエージェントが起動されたときに実際に実行される部分です。ここは大体において標準的な構造に従います。

# meta-data と usage は常に成功で終了します
case $__OCF_ACTION in
meta-data)      foobar_meta_data
                exit $OCF_SUCCESS
                ;;
usage|help)     foobar_usage
                exit $OCF_SUCCESS
                ;;
esac

# meta-data と usage 以外はすべて必ずパラメータ検証を通すようにする
foobar_validate_all || exit $?

# それぞれのアクションを適切な関数呼び出しに変換する
case $__OCF_ACTION in
start)          foobar_start;;
stop)           foobar_stop;;
status|monitor) foobar_monitor;;
promote)        foobar_promote;;
demote)         foobar_demote;;
notify)         foobar_notify;;
reload)         ocf_log info "Reloading..."
                foobar_start
                ;;
validate-all)   ;;
*)              foobar_usage
                exit $OCF_ERR_UNIMPLEMENTED
                ;;
esac
rc=$?

# 必要であればデバッグメッセージのログ出力を行っても良い
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION returned $rc"
exit $rc

5. リソースエージェントのアクション

それぞれのアクションは、リソースエージェントの中で別々の関数もしくはメソッドとして実装するのが一般的です。慣習として、通常は <agent>_<action> という名前をつけます。したがって、foobarstart アクションを実装する関数は foobar_start() という名前にします。

一般論として、リソースエージェントが回復不可能な致命的なエラーに遭遇した場合、即座に終了する、例外を投げる、その他実行を中断することが許されます。この場合の例としては、設定上の問題、バイナリが存在しない、権限の問題、などがあげられます。これらのエラーについては必ずしも呼び出し元にエラーを返してくる必要はありません。

エラーからの復旧動作は、クラスタマネージャがユーザの設定に基づいて適切に実行するべきものです。リソースエージェント側ではその設定についていかなる仮定も持ってはいけません。

5.1. start アクション

start アクションが起動されたときは、リソースエージェントは(リソースがまだ実行されていない場合は)リソースを起動しなければなりません。つまり、エージェントはリソースの設定を検証し、状態を確認し、リソースが実行されていない場合のみリソースを起動する、ということが必要になります。これらを行うためには、以下の例に示すように、最初に validate_allmonitor を実行するのが一般的な方法です。

foobar_start() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースが既に実行中の場合はこの段階で終了する
    if foobar_monitor; then
        ocf_log info "Resource is already running"
        return $OCF_SUCCESS
    fi

    # ここで実際のリソース起動を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ...

    # リソース起動を実行したら、正常に起動したかどうかをチェックする。
    # リソースが非同期で起動する場合は、エージェントはここで monitor
    # 関数を繰り返す。もしリソースが所定のタイムアウト時間以内に起動
    # しなかった場合、クラスタマネージャが start アクションが失敗した
    # と判断する。
    while ! foobar_monitor; do
        ocf_log debug "Resource has not started yet, waiting"
        sleep 1
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

5.2. stop アクション

stop アクションが起動されたときは、リソースエージェントは(リソースが実行中の場合は)リソースを停止しなければなりません。つまり、エージェントはリソースの設定を検証し、状態を確認し、リソースがその時点で実行中の場合のみ停止する、ということが必要になります。これらを行うためには、以下の例に示すように、最初に validate_allmonitor を実行するのが一般的な方法です。ここで覚えておくべき重要なことは、stop は強制的な操作であるということです。つまりリソースエージェントは、その権限でできることのすべてを使って(ノードの再起動やシャットダウンまではできないにしても)リソースを停止しなければならないということです。以下の例を見てください。

foobar_stop() {
    local rc

    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    foobar_monitor
    rc=$?
    case "$rc" in
        "$OCF_SUCCESS")
            # 現在実行中である。正常で想定した動作である。
            ocf_log debug "Resource is currently running"
            ;;
        "$OCF_RUNNING_MASTER")
            # Master として実行中である。停止の前に降格が必要である。
            ocf_log info "Resource is currently running as Master"
            foobar_demote || \
                ocf_log warn "Demote failed, trying to stop anyway"
            ;;
        "$OCF_NOT_RUNNING")
            # 現在停止中である。何もする必要はない。
            ocf_log info "Resource is already stopped"
            return $OCF_SUCCESS
            ;;
    esac

    # ここで実際のリソース停止を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ...

    # リソース停止を実行したら、正常に停止したかどうかをチェックする。
    # リソースが非同期で停止する場合は、エージェントはここで monitor
    # 関数を繰り返す。もしリソースが所定のタイムアウト時間以内に停止
    # しなかった場合、クラスタマネージャが stop アクションが失敗した
    # と判断する。
    while foobar_monitor; do
        ocf_log debug "Resource has not stopped yet, waiting"
        sleep 1
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS

}
Note
stop 操作が成功したときに期待される終了コードは $OCF_SUCCESS です( $OCF_NOT_RUNNING ではありません)。
Important
stop操作が失敗した場合は危険な状況を生み出す可能性があります。この場合クラスタマネージャはほぼ必ずノードを隔離(fencing)することによって問題を解決しようとします。言い換えると、クラスタマネージャは stop 操作が失敗したノードをクラスタから強制的に排除します。この方法は最終的にはデータを保護することに役に立つのですが、アプリケーションやそのユーザに混乱を引き起こします。したがってリソースエージェントは、適切なリソース停止手段をすべて使い果たした場合にのみ、エラー終了とするべきなのです。

5.3. monitor アクション

monitor アクションは、リソースの現在の状態を確認します。このとき次の3種類の状態を判別しなければなりません。

  • リソースは現在実行中です($OCF_SUCCESS を返す)

  • リソースは正常に停止しています($OCF_NOT_RUNNING を返す)

  • リソースに何か問題が発生し、失敗したと判断されました(問題の内容を適切に示す $OCF_ERR_ コードを返す)

foobar_monitor() {
    local rc

    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    ocf_run frobnicate --test

    # この例では、frobnicate は以下の終了コードを返すものとする
    # 0: 実行中であり、master に完全に追いついている
    # 1: 正常に停止している
    # それ以外: エラー
    case "$?" in
        0)
            rc=$OCF_SUCCESS
            ocf_log debug "Resource is running"
            ;;
        1)
            rc=$OCF_NOT_RUNNING
            ocf_log debug "Resource is not running"
            ;;
        *)
            ocf_log err "Resource has failed"
            exit $OCF_ERR_GENERIC
    esac

    return $rc
}

Stateful (master/slave)リソースエージェントでは、Master role を引き受けるのはどのインスタンスが最適であるかを識別するヒントを提供するために、より精密な監視方式を使用することもあります。詳細はmaster優先度の指定で説明します。

Note
クラスタマネージャは、リソースが現在実行中であるかどうかをテストするための probe として、monitor アクションを実行することがあります。通常 monitor オペレーションは、 probe のときと「本物の」monitor アクションのときで全く同一の振る舞いになります。しかし、リソースによって probe のときに特別な扱いを必要とする場合には、この目的のために便利な ocf_is_probe 関数が OCF シェル関数ライブラリで提供されています。

5.4. validate-all アクション

validate-all アクションは、リソースエージェント設定と動作環境が正しいかをテストします。validate-all は、以下のいずれかの戻り値で終了しなければなりません。

  • $OCF_SUCCESS — すべて問題なく、設定も正しく使用可能です。

  • $OCF_ERR_CONFIGURED — ユーザはリソースを正しく設定していません。

  • $OCF_ERR_INSTALLED — リソースは正しく設定されているように思われますが、 validate-all が実行されているノード上に必要なコンポーネントがありません。

  • $OCF_ERR_PERM — リソースは正しく設定されており、必要なコンポーネントもすべてありますが、アクセス権限に関する問題(必要なファイルが作成できないなど)が生じています。

validate-all は通常一つの関数としてまとめられます。この関数は validate-all アクションが明示的に実行されたときのみならず、他のどの関数からも(正常性確認のために)呼び出されます。したがってリソースエージェントの作成者は、この関数は start, stop, monitor オペレーションさらに probe 中にも呼び出されることを念頭に置いておく必要があります。

probe 時にはバリデーション処理において新たな問題が発生します。probe中(すなわちクラスタマネージャが probe 実行時点でリソースは 実行されていない ことを期待しているとき)は、いくつかの必須コンポーネントが該当ノード上で利用できないことが 期待されている 場合があります。たとえば、ストレージデバイス上の共有データは probe 中は読み出せない、などがあります。したがって validate-all 関数は、 ocf_is_probe 便利関数を使って probe を特別に取り扱うことが必要な場合があります。

foobar_validate_all() {
    # 最初に設定のエラーを確認する
    if ! ocf_is_decimal $OCF_RESKEY_eggs; then
       ocf_log err "eggs is not numeric!"
       exit $OCF_ERR_CONFIGURED
    fi

    # 必要なバイナリを確認する
    check_binary frobnicate

    # データディレクトリを確認する(これは共有ストレージ上にある
    # こともあり得るため、probe 中はこの確認は行わない)
    if ! ocf_is_probe; then
       if ! [ -d $OCF_RESKEY_datadir ]; then
          ocf_log err "$OCF_RESKEY_datadir does not exist or is not a directory!"
          exit $OCF_ERR_INSTALLED
       fi
    fi

    return $OCF_SUCCESS
}

5.5. meta-data アクション

meta-data アクションは、リソースエージェントのメタデータを標準出力へ書き出します。この出力は メタデータで規定されているメタデータフォーマットに準拠しなければなりません。

foobar_meta_data {
    cat <<EOF
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="foobar">
  <version>0.1</version>
  <longdesc lang="en">
...
EOF
}

5.6. promote アクション

promote アクションは必須ではありません。stateful リソースエージェント、すなわち MasterSlave という二つの異なる 役割(role) を区別するエージェントでのみ実装する必要があります。Slave は、statelessリソースエージェントでの Started 状態と機能的には同じです。したがって、通常の(stateless)リソースエージェントは startstop のみを実装すれば十分ですが、stateful リソースエージェントは Started (Slave) と Master role の間の遷移を可能とするための promote アクションの実装が必要です。

foobar_promote() {
    local rc

    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースの現在の状態を確認する
    foobar_monitor
    rc=$?
    case "$rc" in
        "$OCF_SUCCESS")
            # slave として実行中。正常であり期待した動作である。
            ocf_log debug "Resource is currently running as Slave"
            ;;
        "$OCF_RUNNING_MASTER")
            # すでに master である。想定外だが問題ではない。
            ocf_log info "Resource is already running as Master"
            return $OCF_SUCCESS
            ;;
        "$OCF_NOT_RUNNING")
            # 現在停止中である。昇格の前に開始が必要である。
            ocf_log info "Resource is currently not running"
            foobar_start
            ;;
        *)
            # リソースの故障。クラスタマネージャに復旧を任せる。
            ocf_log err "Unexpected error, cannot promote"
            exit $rc
            ;;
    esac

    # ここで実際のリソースの昇格を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ocf_run frobnicate --master-mode || exit $OCF_ERR_GENERIC

    # リソースの昇格を実行したら、昇格が動作したかどうかをチェックする。
    # リソースが非同期で昇格する場合は、エージェントはここで monitor
    # 関数を繰り返す。もしリソースが所定のタイムアウト時間以内に Master
    # role とならなかった場合、クラスタマネージャが promote アクションが
    # 失敗したと判断する。
    while true; do
        foobar_monitor
        if [ $? -eq $OCF_RUNNING_MASTER ]; then
            ocf_log debug "Resource promoted"
            break
        else
            ocf_log debug "Resource still awaiting promotion"
            sleep 1
        fi
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

5.7. demote アクション

demote アクションは必須ではありません。stateful リソースエージェント、すなわち MasterSlave という二つの異なる 役割(role) を区別するエージェントでのみ実装する必要があります。Slave は、statelessリソースエージェントでの Started 状態と機能的には同じです。したがって、通常の(stateless)リソースエージェントは startstop のみを実装すれば十分ですが、stateful リソースエージェントは MasterStarted (Slave) role の間の遷移を可能とするための demote アクションの実装が必要です。

foobar_demote() {
    local rc

    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースの現在の状態を確認する
    foobar_monitor
    rc=$?
    case "$rc" in
        "$OCF_RUNNING_MASTER")
            # master として実行中。正常であり期待した動作である。
            ocf_log debug "Resource is currently running as Master"
            ;;
        "$OCF_SUCCESS")
            # すでに slave で実行中である。何もする必要はない。
            ocf_log debug "Resource is currently running as Slave"
            return $OCF_SUCCESS
            ;;
        "$OCF_NOT_RUNNING")
            # 現在停止中である。この状態において demote アクション
            # が実行されるのは想定外である。エラーで終了し、
            # クラスタマネージャに復旧を任せる。
            ocf_log err "Resource is currently not running"
            exit $OCF_ERR_GENERIC
            ;;
        *)
            # リソースの故障。クラスタマネージャに復旧を任せる。
            ocf_log err "Unexpected error, cannot demote"
            exit $rc
            ;;
    esac

    # ここで実際のリソースの降格を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ocf_run frobnicate --unset-master-mode || exit $OCF_ERR_GENERIC

    # リソースの降格を実行したら、降格が動作したかどうかをチェックする。
    # リソースが非同期で降格する場合は、エージェントはここで monitor
    # 関数を繰り返す。もしリソースが所定のタイムアウト時間以内に Slave
    # role とならなかった場合、クラスタマネージャが demote アクションが
    # 失敗したと判断する。
    while true; do
        foobar_monitor
        if [ $? -eq $OCF_RUNNING_MASTER ]; then
            ocf_log debug "Resource still awaiting promotion"
            sleep 1
        else
            ocf_log debug "Resource demoted"
            break
        fi
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

5.8. migrate_to アクション

migrate_to アクションは、以下の2つの目的に対応しています。

  • リソース固有の push タイプのマイグレーションを開始します。つまり、リソースがその時点で実行されているノードから、指定したノード 移動するよう指示します。リソースエージェントは、 $OCF_RESKEY_CRM_meta_migrate_target 環境変数によってその宛先ノードを知ることができます。

  • freeze/thaw (または suspend/resume とも呼ばれる)タイプのマイグレーションにおいて、リソースをフリーズします。このモードでは、リソースはこのとき宛先ノードについての情報を必要としません。

以下の例は、pushタイプのマイグレーションを示しています。

foobar_migrate_to() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースが実行中でない場合、この時点で終了する
    if ! foobar_monitor; then
        ocf_log err "Resource is not running"
        exit $OCF_ERR_GENERIC
    fi

    # ここで実際のリソースの起動を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ocf_run frobnicate --migrate \
                       --dest=$OCF_RESKEY_CRM_meta_migrate_target \
                       || exit OCF_ERR_GENERIC
    ...

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

これに対し、freeze/thawタイプのマイグレーションは以下のようなfreezeオペレーションを実装します。

foobar_migrate_to() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースが実行中でない場合、この時点で終了する
    if ! foobar_monitor; then
        ocf_log err "Resource is not running"
        exit $OCF_ERR_GENERIC
    fi

    # ここで実際のリソースの処理を開始する(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ocf_run frobnicate --freeze || exit OCF_ERR_GENERIC
    ...

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

5.9. migrate_from アクション

migrate_from アクションは、以下の2つの目的に対応しています。

  • リソース固有の push タイプのマイグレーションを完了する。つまり、マイグレーションが正しく終了したかどうか、リソースがローカルノードで実行されているかどうか、をチェックします。リソースエージェントは、マイグレーション元を $OCF_RESKEY_CRM_meta_migrate_source 環境変数によって知ることができます。

  • freeze/thaw (または suspend/resume とも呼ばれる)タイプのマイグレーションにおいてリソースを解凍する。このモードでは、リソースはこのときソースノードについての情報を必要としません。

以下の例は、pushタイプのマイグレーションを示しています。

foobar_migrate_from() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースのマイグレーションを実行したら、正常に再開したかどうかを
    # チェックする。リソースが非同期で開始する場合は、エージェントは
    # ここで monitor 関数を繰り返す。もしリソースが所定のタイムアウト時間
    # 以内に実行されなかった場合、クラスタマネージャが migrate_from
    # アクションが失敗したと判断する。
    while ! foobar_monitor; do
        ocf_log debug "Resource has not yet migrated, waiting"
        sleep 1
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

これに対して、freeze/thawタイプのマイグレーションは以下のようなthawオペレーションを実装します。

foobar_migrate_from() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # ここで実際のリソースの起動を行う(なにか重大な問題が発生した場合は
    # $OCF_ERR_ エラーコードですぐに終了すること)
    ocf_run frobnicate --thaw || exit OCF_ERR_GENERIC

    # リソースのマイグレーションを実行したら、正常に再開したかどうかを
    # チェックする。リソースが非同期で開始する場合は、エージェントは
    # ここで monitor 関数を繰り返す。もしリソースが所定のタイムアウト時間
    # 以内に実行されなかった場合、クラスタマネージャが migrate_from
    # アクションが失敗したと判断する。
    while ! foobar_monitor; do
        ocf_log debug "Resource has not yet migrated, waiting"
        sleep 1
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

5.10. notify アクション

通知を用いることによって、cloneインスタンス(および clone の拡張である master/slave リソース)はその状態をお互いに通知し合うことができます。通知を有効にすると、cloneのすべてのインスタンスのすべてのアクションに対して pre および post 通知が発生します。そしてクラスタマネージャは、すべての クローンインスタンスに対して notify オペレーションを実行します。notify オペレーションに対しては、以下の追加の環境変数がリソースエージェントの実行中に渡されます。

  • $OCF_RESKEY_CRM_meta_notify_type --通知タイプ ( pre または post )

  • $OCF_RESKEY_CRM_meta_notify_operation — 通知の対象となるオペレーション(アクション) ( start , stop , promote , demote など)

  • $OCF_RESKEY_CRM_meta_notify_start_uname — リソースを起動しているノードの名前( start の通知のみ)

  • $OCF_RESKEY_CRM_meta_notify_stop_uname — リソースを停止しているノードの名前 ( stop の通知のみ)

  • $OCF_RESKEY_CRM_meta_notify_master_uname — リソースがその時点で Master role になっている ノードの名前

  • $OCF_RESKEY_CRM_meta_notify_promote_uname — リソースがその時に Master role に昇格している ノードの名前 ( promote の通知のみ)

  • $OCF_RESKEY_CRM_meta_notify_demote_uname — ソースがその時に Slave role に降格している ノードの名前 ( demote の通知のみ)

通知は、"pull" 型を使っている master/slave リソース、すなわち master が発行者で slave が購読者のような場合において特に便利です。master は昇格が発生したときにのみ利用可能となることは明らかなので、slave 側では正しい発行者から購読するよう自分自身を設定するために、"pre-promote" 通知を利用することができます。

同様に、発行者が master 状態では無くなった後に購読者が購読をやめたい場合があり、この目的のために "post-demote" 通知を使うことができます。

この考え方については以下に示す例を参照してください。

foobar_notify() {
    local type_op
    type_op="${OCF_RESKEY_CRM_meta_notify_type}-${OCF_RESKEY_CRM_meta_notify_operation}"

    ocf_log debug "Received $type_op notification."
    case "$type_op" in
        'pre-promote')
            ocf_run frobnicate --slave-mode \
                               --master=$OCF_RESKEY_CRM_meta_notify_promote_uname \
                               || exit $OCF_ERR_GENERIC
            ;;
        'post-demote')
            ocf_run frobnicate --unset-slave-mode || exit $OCF_ERR_GENERIC
            ;;
    esac

    return $OCF_SUCCESS
}
Note
master/slaveリソースエージェントは、同時に複数の master が存在できる マルチマスタ 設定をサポートすることができます。この場合 $OCF_RESKEY_CRM_meta_notify_*_uname 変数は、例に示したような一つのホスト名ではなく、空白文字で区切られたホスト名のリストを持ちます。こういった状況では、リソースエージェントはこのリストを正しく繰り返し処理しなければなりません。
Note
訳注: 上記のコード例では、master 側からの通知を受けて slave 側で実行する処理を意図しているようにも読めますが、実際には master 側でも通知を受け取って同じ処理が実行されるので、実装する際は適切な処理が行われるよう注意してください。

6. スクリプト変数

本項では、リソースエージェントで一般に利用可能な、主に利便性を目的とした変数の概要について説明します。エージェントの実行中に利用できるその他の変数については、環境変数戻り値を参照してください。

6.1. $OCF_RA_VERSION_MAJOR

クラスタマネージャが現在使用しているリソースエージェントAPIのメジャーバージョン番号。

6.2. $OCF_RA_VERSION_MINOR

クラスタマネージャが現在使用しているリソースエージェントAPIのマイナーバージョン番号。

6.3. $OCF_ROOT

OCFリソースエージェントのディレクトリ階層のルート。これはリソースエージェントが変更してはいけません。通常は /usr/lib/ocf となります。

6.4. $OCF_FUNCTIONS_DIR

リソースエージェントシェル関数ライブラリ( ocf-shellfuncs )が配置されているディレクトリ。これは通常、 $OCF_ROOT を元に定義されており、リソースエージェントが変更してはいけません。しかしこの変数は、新規または修正リソースエージェントをテストするときに、コマンドラインから上書きされる場合があります。

6.5. $OCF_EXIT_REASON_PREFIX

リソースエージェントからのエラーメッセージを表示するときに使われるプレフィックス。スクリプト関数はこれを内部的に使うので、シェルベースのスクリプトでは明示的に利用する必要はありません。

6.6. $OCF_RESOURCE_INSTANCE

リソースインスタンス名。primitive (clone でも stateful でもない) リソースに関しては、これは単なるリソース名となります。clonesおよびstatefulリソースに対しては、これはprimitive名の後にコロンとcloneインスタンス番号(例: p_foobar:0 )が続きます。

6.7. $OCF_RESOURCE_TYPE

現在のリソースのリソース種別。例: IPaddr2。

6.8. $OCF_RESOURCE_PROVIDER

リソースプロバイダ。例: heartbeat。 リソースエージェント API バージョン 1.0 では、クラスタマネージャによっては設定されていないことがあります。

6.9. $__OCF_ACTION

現在起動されているアクション。これは、クラスタマネージャがリソースエージェントを起動するときに指定した1つ目のコマンドライン引数と同一です。

6.10. $__SCRIPT_NAME

リソースエージェントの名前。これは、先行するディレクトリ名を削除したリソースエージェントスクリプトのベース名と同一です。

6.11. $HA_RSCTMP

リソースエージェントにより使用される一時ディレクトリです。システム起動シーケンスにおいて(LSB準拠のLinuxディストリビューションでは)、このディレクトリはシステム起動時に空になっていることが保障されています。したがってノードが再起動した後は、このディレクトリに古いデータが残存していることはありません。

7. 便利関数

7.1. ログ出力: ocf_log

リソースエージェントでは、ログ出力を行いたい場合は ocf_log 関数を使ってください。この便利なログラッパーは以下のように実行します。

ocf_log <severity> "Log message"

以下の重要度レベル(severity)をサポートします。

  • debug — デバッグメッセージ用。ほとんどのログ設定ではデフォルトでこのレベルを抑制します。

  • info — エージェントの振る舞いや状態に関する情報メッセージ。

  • warn — 警告用。これは、回復不能なエラー ではない 想定外の動作を表すメッセージです。

  • err --エラー用。このログレベルは原則的に、適切なエラーコードと合わせて exit の直前でのみ使われるべきです。

  • crit — クリティカルエラー用。 err と同様、このログレベルはリソースエージェントがエラーコードで終了するときでない限り使うべきではありません。ごく稀にしか使われません。

7.2. バイナリのテスト: have_binary および check_binary

リソースエージェントは、特定の実行ファイルが利用可能であるかのテストを必要とする場合があります。この場合 have_binary 関数が便利です。

if ! have_binary frobnicate; then
   ocf_log warn "Missing frobnicate binary, frobnication disabled!"
fi

バイナリのないことがリソースに対して致命的な問題となる場合は、check_binary 関数を使います。

check_binary frobnicate

check_binary は、指定した実行ファイルの存在(および実行可能であること)の確認と、それが存在しないもしくは実行できない場合に $OCF_ERR_INSTALLED で終了することを簡易に記述する方法です。

Note
テストするバイナリの指定がフルパスでない場合、have_binary および check_binary はいずれも $PATH を参照します。バイナリのインストールパスはディストリビューションやユーザの方針によって異なるため、通常はフルパスでのテストは しない ほうが良いでしょう。

7.3. コマンドの実行とその出力の記録: ocf_run

リソースエージェントがコマンドを実行し、その出力を記録したい場合は、ocf_run 便利関数を使ってください。以下の例のように実行します。

ocf_run frobnicate --spam=eggs || exit $OCF_ERR_GENERIC

上記に指定したコマンドの場合、リソースエージェントは frobnicate --spam=eggs を実行し、その出力と終了コードを記録します。終了コードが0以外(エラーを示す)の場合、ocf_run はコマンドの出力をログの重要度 err としてログ出力し、その後リソースエージェントは終了します。終了コードが0(成功を示す)の場合、コマンドの出力はログの重要度 info でログ出力します。

コマンド実行が成功したときの出力は無視したい、という場合は、ocf_run-q オプションが使えます。下記の例では ocf_run はコマンドの終了コードが0以外の場合のみログを出力します。

ocf_run -q frobnicate --spam=eggs || exit $OCF_ERR_GENERIC

最後に、ゼロ以外の終了コードのときのコマンド出力をエラー_以外_の重要度でログ出力を行いたい場合、ocf_run-info または -warn オプションを追加することでできます。

ocf_run -warn frobnicate --spam=eggs

7.4. ロック: ocf_take_lock および ocf_release_lock_on_exit

一つのクラスタ設定の中で同じ種類のリソースが別々に存在し、それらのアクションが並列で実行されてはならない、という場合が存在します。同じマシン上でアクションが同時実行されないようにするために、リソースエージェントは ocf_take_lockocf_release_lock_on_exit 便利関数を使うことができます。

LOCKFILE=${HA_RSCTMP}/foobar
ocf_release_lock_on_exit $LOCKFILE

foobar_start() {
    ...
    ocf_take_lock $LOCKFILE
    ...
}

ocf_take_lock は、指定された $LOCKFILE を獲得しようとします。これができなかった場合、0秒から1秒の間のランダムな時間スリープし、そしてリトライします。ocf_release_lock_on_exit はエージェントが終了するときに(どんな場合でも)ロックファイルを解放します。

7.5. 数値のテスト: ocf_is_decimal

特にパラメータの検証処理において、指定された値が数値であるかどうかをテストすることは有用です。このために ocf_is_decimal 関数があります。

foobar_validate_all() {
    if ! ocf_is_decimal $OCF_RESKEY_eggs; then
        ocf_log err "eggs is not numeric!"
        exit $OCF_ERR_CONFIGURED
    fi
    ...
}

7.6. 論理値のテスト: ocf_is_true

リソースエージェントがbooleanパラメータを定義している場合、このパラメータに対してユーザが指定できる値には 0/1, true/false, on/off があります。しかしこれらすべての値をリソースエージェントの中でテストするのは面倒なので、代わりに ocf_is_true 便利関数を使ってください。

if ocf_is_true $OCF_RESKEY_superfrobnicate; then
    ocf_run frobnicate --super
fi
Note
ocf_is_true は、空の変数あるいは存在しない変数に対して使われると、常に 1 の終了コードを返します。これは false と同じです。

7.7. バージョン比較: ocf_version_cmp

リソースエージェントではインストールされているソフトウェアのバージョン番号をチェックしたくなる場合があります。ocf_version_cmp は必要な処理を全て面倒見てくれます。

リターンコードは以下です。

  • 0 — 一つ目のバージョンは二つ目のバージョンより小さい(早い)

  • 1 — 二つのバージョンは同一である

  • 2 — 一つ目のバージョンは二つ目のバージョンより大きい(遅い)

  • 3 — どちらかの引数がバージョン文字列として認識できなかった

バージョン番号には数字、ドット、ダッシュが含まれることが許されます。

local v=`gooey --version`
ocf_version_cmp "$v" 12.0.8-1
case $? in
        0) ocf_log err "we do not support version $v, it is too old"
           exit $OCF_ERR_INSTALLED
        ;;
        [12]) ;; # we can work with versions >= 12.0.8-1
        3) ocf_log err "gooey produced version <$v>, too funky for me"
           exit $OCF_ERR_INSTALLED
        ;;
esac

7.8. 疑似リソース: ha_pseudo_resource

「疑似リソース」というのは、実際に実行可能なプロセスのようなものを start したり stop したりするのではなく、単にアクションを実行し、そのアクションが実行されたか否かを何らかの形式で追跡することが必要なリソースエージェントのことです。この例としては portblock リソースエージェントがあります。

疑似リソースのためのリソースエージェントでは ha_pseudo_resource 便利関数を使うことができます。この関数はリソースの状態を追いかける 追跡ファイル を使用します。foobar が疑似リソースを管理するものとして作られているとすると、その start アクションは以下のようになります。

foobar_start() {
    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    # リソースが既に実行中の場合はこの段階で終了する
    if foobar_monitor; then
        ocf_log info "Resource is already running"
        return $OCF_SUCCESS
    fi

    # 疑似リソースを開始する
    ha_pseudo_resource ${OCF_RESOURCE_INSTANCE} start

    # リソース起動を実行したら、正常に起動したかどうかをチェックする。
    # リソースが非同期で起動する場合は、エージェントはここで monitor
    # 関数を繰り返す。もしリソースが所定のタイムアウト時間以内に起動
    # しなかった場合、クラスタマネージャが start アクションが失敗した
    # と判断する。
    while ! foobar_monitor; do
        ocf_log debug "Resource has not started yet, waiting"
        sleep 1
    done

    # _すべてが_ 期待通り成功した場合にのみ $OCF_SUCCESS を返却する
    return $OCF_SUCCESS
}

8. 慣習

この章では、長年続くリソースエージェントのリポジトリの中から発生した慣習をまとめて記載しています。リソースエージェントの作成者はこれらの慣習に必ずしも従う必要はありませんが、驚き最小の原則に則り従った方が良いです。慣習に従ったリソースエージェントの方がより理解しやすく、レビューもしやすくなります。

8.1. よく利用されるパラメータ名

パラメータ名の中にはすでにいくつものリソースエージェントで利用されているものがあります。新規にリソースエージェントを作成するときは以下の例にならうのが一般的に良い考えです。

  • binary — サーバのデーモンなど、リソースを主に管理するバイナリ名

  • config — 設定ファイルのフルパス名

  • pid — プロセスID(PID)を保持するファイルのフルパス名

  • log — ログファイルのフルパス名

  • socket — リソースが管理する UNIX ソケットのフルパス名

  • ip — デーモンが使用する IP アドレス

  • port — デーモンが使用する TCP または UDP ポート番号

言うまでもなく、これらのうち実際に使用する意味のあるもののみをリソースエージェントに実装すべきです。

8.2. パラメータのデフォルト値

リソースエージェントパラメータのデフォルトは、 _default 接尾語のついた変数を初期化して設定するようにします。

# デフォルト値
OCF_RESKEY_superfrobnicate_default=0

: ${OCF_RESKEY_superfrobnicate=${OCF_RESKEY_superfrobnicate_default}}
Note
リソースエージェントは、メタデータで required と指定されていないパラメータに対してデフォルトを設定しなければなりません。

8.3. バイナリには PATH を反映する

リソースエージェントでバイナリ名を設定するためのパラメータ(デーモンや監視処理のためのクライアントコマンドなど)を設けたい場合、そのパラメータは PATH 環境変数を反映する方が良いです。フルパスを与えるべきではありません。したがって、以下の方法

# 良い例 -- こうすべき
OCF_RESKEY_frobnicate_default="frobnicate"
: ${OCF_RESKEY_frobnicate="${OCF_RESKEY_frobnicate_default}"}

は、フルパスを指定した以下の例よりもより望ましい方法です。

# 悪い例 -- できれば避ける
OCF_RESKEY_frobnicate_default="/usr/local/sbin/frobnicate"
: ${OCF_RESKEY_frobnicate="${OCF_RESKEY_frobnicate_default}"}

このルールはデフォルト値に対しても当てはまります。

9. 特記事項

9.1. ライセンス

リソースエージェントのコントリビュータは可能な限り、新たなリソースエージェントには GNU General Public License (GPL) バージョン2以降を使用することを 推奨 します。しかしシェル関数ライブラリは(GPL以外のエージェントでも利用可能とするため)GNU Lesser General Public License (LGPL) バージョン2.1以降としてライセンスしており、これを厳密に強制はしていません。

リソースエージェントはそのライセンスをエージェントのソースコード中に明示的に 記述しなければなりません

9.2. ロケール設定

初期化で説明されている通りに ocf-shellfuncs を読み込むと、リソースエージェントの LANG および LC_ALL は自動的に C ロケールに設定されます。こうすることでリソースエージェントは常に C ロケールで動作することが期待できるので、自分で LANG や各種 LC_ 環境変数をリセットする必要はありません。

9.3. 実行中のプロセスに対するテスト

あるプロセスが(プロセスIDが分かっている場合)現在実行中であるかどうかをテストする方法として、0 シグナルを送ってエラーを取得する、という方法がよく用いられます。以下の例のようになります。

if kill -s 0 `cat $daemon_pid_file`; then
    ocf_log debug "Process is currently running"
else
    ocf_log warn "Process is dead, removing pid file"
    rm -f $daemon_pid_file
if
Important
この例よりもより優れた方法として、クライアントプロセスからデーモンに接続して 機能 をテストする方法があります。monitorアクションで例を示しています。

9.4. master 優先度の指定

Stateful (master/slave)リソースは自身の master 優先度 を設定する必要があります。これは、Master role に昇格するのに最適なインスタンスはどれか、クラスタマネージャにヒントを提供するためです。

Important
複数のインスタンスが同一の正の master 優先度を持つことができます。この場合クラスタリソースマネージャは、昇格するリソースエージェントを自動的に選択します。しかし、 すべての インスタンスの master スコアが 0(デフォルト)である場合、クラスタマネージャはどのインスタンスも昇格しません。つまり、少なくとも1つのインスタンスが正のmasterスコアを持つことが重要です。

この目的のために crm_master が重宝します。この便利な crm_attribute のラッパーは、master-$OCF_RESOURCE_INSTANCE という名前でそれが実行されているノードのノード属性を設定し、指定された値をこの属性に与えます。クラスタマネージャはそれを対応するインスタンスの昇格スコアに変換し、昇格選択のスコアの基にします。

Statefulリソースエージェントは、monitorあるいはnotifyアクションの中で crm_master を実行するのが一般的です。

以下の例は、foobar リソースエージェントが、以下のいずれかに基づいて終了コードが返却されるバイナリを実行することによってアプリケーションの状態をテストすることができると仮定した例です。

  • リソースが master role である、もしくは slave であるが master に追いついている(いかなる意味でも最新データを持っている)。あるいは、

  • リソースが slave role であるが、なんらかの形で非同期レプリケーションが master より「遅れている」。あるいは、

  • リソースは正常に停止している。あるいは、

  • リソースは予期しない故障が発生している。

foobar_monitor() {
    local rc

    # 設定が不正な場合は即終了する
    foobar_validate_all || exit $?

    ocf_run frobnicate --test

    # この例では、frobnicate は以下の終了コードを返すものとする
    # 0: 実行中であり、master に完全に追いついている
    # 1: 正常に停止している
    # 2: 実行中であるが、master より遅れている
    # それ以外: エラー
    case "$?" in
        0)
            rc=$OCF_SUCCESS
            ocf_log debug "Resource is running"
            # 高い master 優先度を設定する。現在の master は
            # 常にこの値 + 1 を持つ。現在 slave である場合は
            # 高い優先度を持ち、master が故障した場合に次の順番で
            # 引き継げるようにする。
            crm_master -l reboot -v 100
            ;;
        1)
            rc=$OCF_NOT_RUNNING
            ocf_log debug "Resource is not running"
            # このノードの master 優先度を削除する
            crm_master -l reboot -D
            ;;
        2)
            rc=$OCF_SUCCESS
            ocf_log debug "Resource is lagging behind master"
            # 低い master 優先度を設定する。もしこの時点で master が
            # 故障した場合、master から遅れていない他の slave が
            # 存在する場合は、より高い master 優先度が勝ち、その
            # slave が新たな master となるようにする。
            crm_master -l reboot -v 5
            ;;
        *)
            ocf_log err "Resource has failed"
            exit $OCF_ERR_GENERIC
    esac

    return $rc
}

10. リソースエージェントをテストする

本項では、リソースエージェントの自動テスト方法について説明します。テストは開発における重要な要素です。リソースエージェントを新規に作成する場合、既存のリソースエージェントを修正する場合、どちらに対しても重要です。

10.1. ocf-tester を用いたテスト

リソースエージェントのリポジトリ(およびインストールされたリソースエージェントパッケージ)には、 ocf-tester という名前のユーティリティが含まれています。このシェルスクリプトによって、リソースエージェントの機能を効率的かつ簡単にテストすることができます。

ocf-tester は一般的には、root によって以下のように起動します。

ocf-tester -n <name> [-o <param>=<value> ... ] <resource agent>
  • <name> は、任意のリソース名です。

  • <param>=<value> は、 -o オプションで、テスト用に設定したいリソースパラメータに応じていくつでも設定できます。

  • <resource agent> はリソースエージェントへのフルパスです。

ocf-tester を起動すると、すべての必須アクションを実行し、アクションがリソースエージェントアクションで説明している通りの動作となっていることを強制します。

また、オプショナルなアクションに対するテストも行います。オプショナルアクションは記載されている場合は期待通り動作する必要がありますが、もし実装されていなくても ocf-tester はエラーとは見なしません。

Important
ocf-tester は、アクションの「予行演習(dry run)」を行うものではありません。またリソースのなんらかのダミーを作成するものでもありません。そうではなく、これは実際のリソースエージェントをそのまま実行するものです。つまりデータベースのオープンやクローズ、ファイルシステムのマウント、仮想マシンの起動や停止、などが行われることもあります。注意して利用してください。

例として、foobar リソースエージェントに対しては以下のように ocf-tester を実行します。

# ocf-tester -n foobartest \
             -o superfrobnicate=true \
             -o datadir=/tmp \
             /home/johndoe/ra-dev/foobar
Beginning tests for /home/johndoe/ra-dev/foobar...
* Your agent does not support the notify action (optional)
* Your agent does not support the reload action (optional)
/home/johndoe/ra-dev/foobar passed all tests

リソースエージェントの振る舞いがよく把握できない動作をした場合、開発したばかりのソフトウェアではたいていそうなりますが、-v オプションや -d オプションを使ってより多くの出力を出すことができます。これだけではよくわからない場合、ocf-tester-X を与えることでリソースエージェントのトレースを行うことができます(出力をファイルにリダイレクトするのを忘れないこと。あなたが超速で読める人でない限り)。

10.2. ocft を用いたテスト

ocft はリソースエージェント用のテストツールです。ocf-tester との大きな違いは、ocft では複雑なテスト環境の作成を自動化している点です。パッケージのインストールや任意のシェルスクリプト実行も可能です。

10.2.1. ocft のコンポーネント

ocft には以下のコンポーネントがあります。

  • テストケースジェネレータ (/usr/sbin/ocft) — テストケース設定ファイルからシェルスクリプトを生成する

  • 設定ファイル (/usr/share/resource-agents/ocft/configs/) — あるリソースエージェントに対する環境設定とテストケースを含む設定ファイル

  • テスト用スクリプトが格納される /var/lib/resource-agents/ocft/cases/ 、ただし通常この内容を知る必要はありません。

10.2.2. テスト環境のカスタマイズ

ocft はリソースエージェントの実行環境を、環境変数を変更する(OCF で規定されているインタフェースによって)、またはその場限りのシェルスクリプトを実行する(例えばファイルのパーミッションを変更したりファイルシステムを unmount したりなど)によって変更します。

10.2.3. テストの実行方法

あなたはテストを実行したいソフトウェア(リソース)についてよく知っている必要があります。全ての必要なシナリオについて、全ての期待する動作条件、期待範囲外の動作条件、それらに対してリソースエージェントが取るべきリアクション、などのスケッチを描きます。そしてこれらの動作条件と期待する動作結果を ocft テストケースとしてコード化する必要があります。ocft の実行は簡単です。

# ocft make <RA>
# ocft test <RA>

一つ目のコマンドはテストケースに対するスクリプトを生成しており、二つ目のコマンドでそれを実行して結果を確認しています。

10.2.4. ocft 設定ファイル構文

4つのトップレベルオプションが存在しており、それぞれに複数のサブオプションが存在します。

CONFIG (トップレベルオプション)

このオプションはグローバルであり、全てのテストケースに対して影響を与えます。

  • AgentRoot (サブオプション)

AgentRoot /usr/lib/ocf/resource.d/xxx

通常はリソースエージェントの場所は heartbeat プロバイダ配下にあるという前提になっています。テストしたいエージェントがそれ以外のベンダによって配布されている場合 AgentRoot を設定します。

  • InstallPackage (サブオプション)

InstallPackage package [package2 [...]]

テストに必要なパッケージをインストールします。必要なパッケージがすでにインストールされている場合はスキップします。

  • HangTimeout (サブオプション)

HangTimeout secs

一回の RA 実行に許される最大時間です。この時間を超過した場合、実行は失敗したとみなされます。

SETUP-AGENT (トップレベルオプション)
SETUP-AGENT
  bash commands

RAのテスト実行前に初期化が必要な場合、そのための bash コードをここに書くことができます。初期化は実行は一回のみです。もし再度初期化の実行が必要な場合は、/tmp/.[AGENT_NAME]_set スタンプファイルを削除してください。

CASE (トップレベルオプション)
CASE "description"

これがテストスイートのメインの構成要素です。一つのテストケースを一つの CASE トップレベルオプションとして記述します。

一つのテストケースは通常複数のサブオプションで構成され、その後に RunAgent サブオプションが続きます。

  • Var (サブオプション)

Var VARIABLE=value

リソースエージェントの環境変数を設定します。通常は OCF_RESKEY_xxx を設定します。一点注意点として、"=" の両サイドには空白を入れないでください。

  • Unvar (サブオプション)

Unvar VARIABLE [VARIABLE2 [...]]

環境変数を削除します。

  • Include (サブオプション)

Include macro_name

macro_name の文をインクルードします。以下の CASE-BLOCK の説明を参照してください。

  • Bash (サブオプション)

Bash bash_codes

このオプションは OS の環境を設定するために使用します。ここに BASH のコードを挿入することでシステムを如何様にでもカスタマイズできます。システムが復旧不可能となる結果を引き起こさないように注意してください。

  • BashAtExit (サブオプション)

BashAtExit bash_codes

このオプションは、次のテストケースを正常に実行できるように OS 環境を復旧させるために使用します。もちろん Bash オプションを復旧のために使用することもできます。しかし、もし処理中になんらかの間違いが発生した場合、スクリプトは復旧用のコードを実行せずすぐに終了してしまいます。このような場合は、終了前にシステム環境を復旧させることができる BashAtExit を使用すべきです。

  • RunAgent (サブオプション)

RunAgent cmd [ret_value]

このオプションはリソースエージェントを実行します。"cmd" は、start, status, stop… などのリソースエージェントのパラメータです。二つ目のパラメータは省略可能です。指定することで、リソースエージェントの実行時に実際の返却値と期待する返却値を比較することができるようになります。もし異なっていればバグが見つかったということです。

また、サブオプションをローカルではなくリモートホストで実行することも可能です。プロトコルには ssh が使用され、コマンドはバックグラインドで実行されます。サブオプション名に @<ipaddr> 接尾辞を付与するだけです。例えば、

Bash@192.168.1.100 date

は、date コマンドを実行します。リモートコマンドはバックグラウンドで実行されます。

要注意: ssh による自動化は、事前に環境を知ることができないためできるとは限りません。"node2" のような「よく利用される」ホスト名を使用すればよいかも? またコマンドはバックグラウンドで実行されるため、終了コードがチェックできるかも不明です。最後に、Var@node には意味があるのか? あるいは現在の環境を何らかの方法でリモートへコピーする? おそらくここには実例が必要でしょう。

全般的に実例が必要です。

CASE-BLOCK (トップレベルオプション)
CASE-BLOCK macro_name

CASE-BLOCK オプションは、 CASE から Include されるマクロを定義します。CASE の全てのサブオプションが CASE-BLOCK で利用可能です。

11. リソースエージェントのインストールとパッケージング

本項では、リソースエージェントの作成とテストが終わった後何をすべきか、つまりどこにインストールして、どのようにあなた独自のアプリケーションパッケージもしくは Linux-HA リソースエージェントリポジトリに含めるのか、について説明します。

11.1. リソースエージェントのインストール

リソースエージェントをあなた独自のプロジェクトの一部に含めたい場合、それに適した場所にインストールしてください。この場合リソースエージェントは、 /usr/lib/ocf/resource.d/<provider> ディレクトリにインストールします。ここで <provider> は、あなたのプロジェクトの名前、もしくはリソースエージェントを識別するための何らかの名前を入れます。

たとえば、 foobar リソースエージェントを fortytwo という名前のプロジェクトの一部としてパッケージしようとしている場合、このリソースエージェントの正しいフルパスは /usr/lib/ocf/resource.d/fortytwo/foobar となります。リソースエージェントは 0755 (-rwxr-xr-x)パーミッションビットでインストールすることを忘れないでください。

この方法に従ってインストールを行うことで、 OCF準拠クラスタリソースマネージャは、リソースエージェントを正しく識別、解釈、実行できるようになります。たとえば上記に示したインストールパスは、Pacemakerクラスタマネージャでは ocf:fortytwo:foobar リソースタイプ識別子に対応付けされます。

11.2. リソースエージェントのパッケージング

パッケージリソースをあなた独自のプロジェクトの一部としてパッケージする場合は、本項で概説する注意事項に従ってください。

Note
もし、リソースエージェントを Linux-HA リソースエージェントリポジトリに投稿したい場合、リソースエージェントの投稿で必要な情報を参照してください。

11.2.1. RPMパッケージング

OCFリソースエージェントは 、<toppackage>-resource-agents という名前でRPMサブパッケージに入れることを推奨します。このとき、自分のプロバイダディレクトリをそのパッケージが所有し、ディレクトリ階層や便利シェル関数を提供しているアップストリーム resource-agents パッケージへの依存関係を持つようにしてください。以下に例として RPM スペックファイルの一部抜粋を示します。

%package resource-agents
Summary: OCF resource agent for Foobar
Group: System Environment/Base
Requires: %{name} = %{version}-%{release}, resource-agents

%description resource-agents
This package contains the OCF-compliant resource agents for Foobar.

%files resource-agents
%defattr(755,root,root,-)
%dir %{_prefix}/lib/ocf/resource.d/fortytwo
%{_prefix}/lib/ocf/resource.d/fortytwo/foobar
Note
RPM スペックファイルに %package 宣言を記述すると、RPMはそれを Name , Version , License などの項目をトップレベルから継承したサブパッケージであると認識します。サブパッケージは、その名前の前にトップレベルパッケージの名前を自動的に付加します。したがって、上記の抜粋の例では(パッケージ名 Namefoobar であるとすると) foobar-resource-agents という名前のサブパッケージを作成します。

11.2.2. Debianパッケージング

Debianパッケージの場合、RPMと同じようにリソースエージェントを保持する別のパッケージを作成することを推奨しますが、この場合は cluster-agents パッケージへの依存関係を持つ必要があります。

Note
本項では debhelper でパッケージングすることを前提とします。

以下に debian/control の一部抜粋の例を示します。

Package: foobar-cluster-agents
Priority: extra
Architecture: all
Depends: cluster-agents
Description: OCF-compliant resource agents for Foobar

また、これとは別に .install ファイルも作成します。 foobar リソースエージェントを fortytwo のサブパッケージとしてインストールする例に従うと、debian/fortytwo-cluster-agents.install ファイルは以下の内容で構成されます。

usr/lib/ocf/resource.d/fortytwo/foobar

11.3. リソースエージェントの投稿

リソースエージェントを自分独自のパッケージではなく、アップストリームのリソースエージェントリポジトリ(GitHub 上の ClusterLabs リポジトリ)に投稿したい場合は、本項で説明する手順に従ってください。

まず、以下のコマンドで、アップストリームリポジトリの作業用コピー (Git clone) を作成してください。

git clone git://github.com/ClusterLabs/resource-agents

そしてあなたのリソースエージェントを heartbeat サブディレクトリにコピーしてください。

cd resource-agents/heartbeat
cp /path/to/your/local/copy/of/foobar .
chmod 0755 foobar
cd ..

次に、resource-agents/heartbeat 配下の Makefile.am ファイルを編集し、あなたの新リソースエージェントを ocf_SCRIPTS リストに追加してください。これによりエージェントが正しくインストールされるようになります。

最後に resource-agents/doc/man 配下の Makefile.am を開き 、ocf_heartbeat_<name>.7man_MANS 変数に追加してください。これにより、リソースエージェントのマニュアルページがメタデータから自動的に作成され、そのマニュアルページが正しい場所にインストールされるようになります。

そしてここで、あなたの新しいリソースエージェントと二つの Makefile に行った修正をチェンジセットに追加します。

git add heartbeat/foobar
git add heartbeat/Makefile.am
git add doc/man/Makefile.am
git commit

コミットメッセージには以下のような分かりやすい説明を記述してください。

High: foobar: new resource agent

This new resource agent adds functionality to manage a foobar service.
It supports being configured as a primitive or as a master/slave set,
and also optionally supports superfrobnication.

これで、メーリングリスト上でレビューするためのパッチセットが準備できました。

git send-email --to=linux-ha-dev@lists.linux-ha.org

git send-email は、手元で行われた修正でアップストリームリポジトリに含まれていないものを全てまとめて、メール用に適切なフォーマットを行い、メーリングリストに投稿します。git send-email を利用する際の詳細な設定については man git-send-email を参照してください。

あなたの新リソースエージェントのマージが受諾されれば、アップストリーム開発者の誰かがあなたのパッチをアップストリームリポジトリに push するでしょう。ここまでくれば、アップストリームからのチェックアウトを更新し、あなたの作成したパッチセットを削除することができます。

git reset --hard origin/master
git pull

11.4. リソースエージェントの維持管理

あなたが何かのリソースエージェントの管理を行ったり、コードベースに継続的にコントリビューションを行う場合は、GitHub 上であなた専用の ClusterLabs/resource-agents リポジトリの フォーク(fork) を管理するのが一般的で良い方法です。

このためには以下を行います。

リソースエージェントの作業を行う場合、ぜひ「こまめにcommit、頻繁にcommit」を心がけてください。commit は後からいつでも git rebase -i でひとまとめにすることができます。

必要な修正が完了し他の人にレビューして欲しいと思ったら、その修正をあなたの GitHub のフォークにプッシュして、linux-ha-dev メーリングリストに投稿します。

レビューが完了したら、必要な要望を反映しツリーを整理して、そしてプルリクエストを発行します。これには二つの方法があります。

  • git request-pull コマンドを使って、あなたの修正をまとめて要約したメールの雛形を作成することができます。これに必要な情報を追加してメーリングリストに送信してください。メールの件名に [GIT PULL] のプレフィックスを付与するのは良い方法です。アップストリームメンテナがメッセージを見つけやすくなります。

  • GitHub 上で直接プルリクエストを発行することもできます。GitHub は新しいプルリクエストがあったことをアップストリームメンテナに自動的にメールで通知します。プルリクエストの発行に関する詳細については github:help を参照してください。