nova-hooks によるイベントハンドリング

 こんにちは、インフラストラクチャ本部の大山裕泰です。今日は (恐らく) あんまり知られていない OpenStack のマイナー機能を紹介したいと思います。今日の内容は nova-hooks を用いたイベントハンドリングになります。これの仕組みと実装、そして実際に動作させた際にどうなるのかまで見てゆきます。尚、今回利用した OpenStack の環境は Juno (2014.2.1-0ubuntu1~cloud0) になります。

nova-hooks ってなに?

 nova-hooks によって、OpenStack 本体の nova.hooks がデコレートされたメソッドに対して、任意の処理を関連づけることができます。具体的には、nova.hooks がデコレートされたメソッドに紐づいたユーザ定義の Python パッケージをインストールすることで、nova-hooks は当該メソッドが呼ばれる前と後にユーザ定義の処理を実行します。この辺りの仕組みはとても洗練されており、後で具体的なコードを交えながら解説してゆきます。

何がうれしいの?

 現在 nova-hooks がデコレートされているメソッドは、nova-api がインスタンスの作成時に実行するメソッド (create) と、同じく nova-api がインスタンスの削除要求を受け取った際に実行する内部メソッド (_delete_instance) の2つが存在します。またこの他にも nova-network においてインスタンスのネットワークが有効になった際に呼び出されるメソッド (update_instance_cache_with_nw_info) もあります。
 例えば「インスタンスが作成 (削除) された際に、課金システムと連携して当該インスタンスを利用したユーザへ請求を更新するなど、インスタンスの作成・削除に連動して外部のシステムやサービスと連携する際に便利かと思います。

どんなふうに使えるの?

 では実際にどんな風に使えて、どんな事が出きるのかをコードを交えながら見てゆきます。ここでは例としてインスタンス作成時に呼び出されるメソッドに対して、「入力情報のログを吐き出す」処理を行うクラスを関連付けます。
 nova-api に対してインスタンス作成要求が到着すると、以下 nova.compute.api.API の create メソッドが呼び出されます。

 
 create メソッドにデコレータ式 add_hook が適応されています。これが指定されている全てのメソッドに対し、後述する方法によってユーザ定義のクラスを紐付けることができます。
 add_hook に渡されている引数 "create_instance" は、nova-hooks に対してユーザ定義のクラスをこの create メソッドに紐付けるための識別子になります。
 それでは、ユーザ定義クラスとユーザ定義クラスを nova-hooks に登録する処理について見てゆきます。

ユーザ定義クラスの作成

 まずはユーザ定義クラスの実装について見てゆきます。以下が nova.compute.api.API クラスの create メソッドをフックするユーザ定義クラス (MyCreateHook) になります。

 MyCreateHook には、nova.compute.api.API クラスの create メソッドが実行される前に呼び出される (pre) メソッドと実行後に呼び出される (post) メソッドの 2 つだけが定義されています。
 各メソッドの (*args, **kwargs) という引数の形式は python で汎用的な可変長引数を取るメソッドを実装する時の定石のようで、呼び出し側でリスト形式とハッシュ形式で引数を渡した場合においても、それぞれ args と kwargs で受け取れるので統一的な表記で実装できるようになっています。
 MyCreateHook の pre/post の各メソッドではそれぞれ、nova.openstack.common.log モジュール を使って引数を OpenStack のログに出力しています。
 次にこれを登録し、インスタンス作成時に MyCreateHook で処理を受け取れるところまでを見てゆきます。

ユーザ定義クラスの登録 

 nova-hooks に MyCreateHook クラスを登録するには python パッケージを作成し、パッケージのどのクラスを nova-hooks にデコレートされたメソッドに紐付けるかの設定をそれぞれ python のパッケージ管理機構 setuptools を使って行います。
 以下では、MyCreateHook クラスを含む nova_hooks_example パッケージを作成しています。

尚、ディレクトリツリーは以下のようになっています。

 Python には、動的なサービスやプラグイン拡張の仕組み (https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins) を提供しており、setuptools では、entry_points パラメータによって、アプリケーションに対してコールバックルーチンを渡す事ができます。
 nova-hooks では、上記のようにデコレータ式 add_hook の引数で指定した名前と、ユーザ定義クラス名をそれぞれ key と value に指定することで処理の紐付けを行っています。

動かしてみた

 それでは nova_hooks_example パッケージを生成、及びインストールを実施します。

 アンインストール作業は、単純にインストールしたパッケージファイル (/usr/local/lib/python2.7/dist-packages/nova_hooks_example-0.0.0-py2.7.egg) を削除する感じになります。
 この状態で nova-api を再起動してインスタンスを作成すると以下の内容が nova-api のログ (/var/log/nova/nova-api.log) に出力されます。

 * pre メソッドの出力
 * post メソッドの出力

終わりに

 いかがだったでしょうか。MyCreateHook の pre/post メソッドによってインスタンス作成 (削除) に連動していろいろ出来そうな気がしてきたんじゃないでしょうか。
 今後も OpenStack の小ネタをちょこちょことご紹介できればと思います。