http://cloudstack.jp/wp-content/uploads/2014/02/ccc.japan_32.jpg3/6にCloudStack Day Japan 2014というCloudStackについてのカンファレンスが東京のソラシティ カンファレンスセンターで開催されるそうです。Apache CloudStackのPMC(Project Management Committee)メンバーのChip ChildersやHugo Trippaersも来日してセッションを持つそうなので、非常に期待が持てますね。そのCloudStack Day Japan関連のBlogイベントである、CloudStack Blogコンテストに、このエントリは参加しています。

 

以前、「CloudStack開発ことはじめ」と題して、CloudStackの開発環境をセットアップする手順として、IntelliJ IDEAでCloudStackをビルド、デバックしたり、JenkinsでCloudStackのパッケージをビルドする手順を紹介しました(1) (2) (3)。ビルド、デバッグ、パッケージングまできたら、次のテーマはテストでしょう、ということで、今回のエントリでは、Marvinを利用した、CloudStackの結合テストの自動化について解説を行います。

Marvinとは

Marvin is the CloudStack automation framework. It originated as a tool for integration testing but is now also used to build DevCloud as well as to provide a Python CloudStack API binding.

CloudStack Developer Guide

…ということで、MarvinとはCloudStackの自動化フレームワークです。今回目的の結合テストだけでなく、DevCloudや、Pythonの CloudStack API bindingにも使われているようですね。

 

環境構成

今回は、このMarvinを用いて、DevCloudではない、物理ホスト上にデプロイされたCloudStackに対して、結合テストを実行することをゴールとします。DevCloudも手軽でよいのですが、より信頼性の高いテスト結果を得るためには、結合テストはサービスを実際に展開する構成と揃えたテスト環境で実行したいものです。ということで、今回は、既に物理サーバー上に構築済みのCloudStack環境に対してMarvinで結合テストを走らせることを目的とします。

Marvinのインストール

git clone https://github.com/apache/cloudstack.git 
cd cloudstack
mvn -P developer,systemvm clean install
mvn -P developer -pl :cloud-apidoc
mvn -P developer -pl :cloud-marvin

まずは、cloudstackのソースコードを落としてきた上で、MarvinのパッケージをMavenでコンパイルします(紛らわしい。。)ドキュメントによると、依存関係にあるのはAPIdocだけ、という話だったのですが、実行してみたところエラーが出てしまったのでとりあえずCloudStackの普通のコンパイルも行ったうえで再度APIdocとMarvinのパッケージのコンパイルを試みたところ、成功しました。

ls -al tools/marvin/dist
total 256
drwxrwxr-x 2 ynojima ynojima   4096  3月  3 04:58 .
drwxrwxr-x 6 ynojima ynojima   4096  3月  3 05:03 ..
-rw-rw-r-- 1 ynojima ynojima 251359  3月  3 05:03 Marvin-0.1.0.tar.gz

コンパイルに成功すると、tools/marvin/dist以下に、Marvinのパッケージが出来ています。

pip install tools/marvin/dist/Marvin-0.1.0.tar.gz

これを、pipでインストールしてmarvinのインストールは完了です。

Management Serverの設定

さて、Marvinは、CloudStackの処理の自動化と、その結果のAssertのために、CloudStackのAPIキーとCloudStackのデータベースへの接続を必要とします。

RootAdmin権限を持つユーザーのAPIキーを用意しておきましょう。Accounts、Usersと辿っていった先の鍵穴マークのアイコンから生成できます。

image

続いてデータベースへの接続。CloudStackのデータベースをcloud-setup-databasesコマンドでセットアップすると、確か全ソースIP addressからの接続を許可したcloudというユーザーを作成してくれちゃっているので、

iptables -I INPUT -p tcp --dport 3306 -j ACCEPT

iptablesでMySQLのポートを開けてやると、別ホストからMavinを実行出来るようになります。今回は簡単のため、ソースIP addressを絞っていませんが、Management Serverが直接インターネットに面している場合は、ちゃんと絞りましょう。

Marvinの設定ファイル

続いて、Marvinの設定ファイルを用意しましょう。

{
    "dbSvr": {
        "dbSvr": "<mysql server ip address>",
        "passwd": "cloud",
        "db": "cloud",
        "port": 3306,
        "user": "cloud"
    },
    "mgtSvr": [
        {
            "mgtSvrIp": "<management server ip address>",
            "port": <api port>,
            "apiKey": "<api key>",
            "securityKey": "<security key>" // secretkeyではない点注意
        }
    ]
}

Marvinの設定ファイルはJSON形式です。最低限必要な情報は、以上の通りです。前項で用意したAPIキーと、DBアクセス情報を埋めた上で、保存してください。

Marvinのテストコード

MarvinのテストコードはPythonのテストフレームワークである、noseで記述されます。mavinは、noseのプラグインという位置づけで、テストコードの基本的な記述方法は、noseのドキュメントを参考にすると良いでしょう。

また、test/integrationディレクトリ以下に、Marvinで記述されたCloudStackのテストコードが山とありますので、こちらも参考にすると良いでしょう。

一例として、以下にtest/integration/test_global_settings.pyに対してコメントを付けたものを載せます。

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
""" P1 tests for updating the granular Configuration parameter with scope and resource id provided.
"""
#Import Local Modules
from marvin.cloudstackTestCase import *
from marvin.cloudstackAPI import *
from marvin.integration.lib.utils import *
from marvin.integration.lib.base import *
from marvin.integration.lib.common import *
from nose.plugins.attrib import attr
#Import System modules

class TestUpdateConfigWithScope(cloudstackTestCase): #noseでは、Testで始まるクラスがテストケース扱いされます
    """
    Test to update a configuration (global setting) at various scopes
    """
    def setUp(self): #xUnitフレームワークでおなじみのsetUpメソッド。テスト環境の準備をここで記述します。クラスメソッドとしてやれば、各テストメソッド毎の実行ではなく、テストクラス毎に実行されます。
        self.apiClient = self.testClient.getApiClient() #設定ファイルに記述したAPIキーがセットされたAPIClientのインスタンスの取得。getDbClientとすれば、DBのクライアントが取得出来ます。

    @attr(tags=["simulator", "devcloud", "basic", "advanced"]) #テストコードを実行する条件
    def test_UpdateConfigParamWithScope(self):
        """
        test update configuration setting at zone level scope
        @return:
        """
        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() #設定変更APIコマンドの組み立て
        updateConfigurationCmd.name = "use.external.dns"
        updateConfigurationCmd.value = "true"
        updateConfigurationCmd.scopename = "zone"
        updateConfigurationCmd.scopeid = 1

        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) #APIコマンドの呼び出し
        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))

        listConfigurationsCmd = listConfigurations.listConfigurationsCmd()
        listConfigurationsCmd.cfgName = updateConfigurationResponse.name
        listConfigurationsCmd.scopename = "zone"
        listConfigurationsCmd.scopeid = 1
        listConfigurationsResponse = self.apiClient.listConfigurations(listConfigurationsCmd) #先ほど変更した設定の読み出し

        self.assertNotEqual(len(listConfigurationsResponse), 0, "Check if the list API \
                            returns a non-empty response")

        for item in listConfigurationsResponse:
            if item.name == updateConfigurationResponse.name:
                configParam = item

        self.assertEqual(configParam.value, updateConfigurationResponse.value, "Check if the update API returned \
                         is the same as the one we got in the list API") #変更時のAPIの戻り値とListした結果の比較


    def tearDown(self): #xUnitフレームワークでおなじみのtearDownメソッド。変更した設定の取り消し
        """
        Reset the configuration back to false
        @return:
        """
        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
        updateConfigurationCmd.name = "use.external.dns"
        updateConfigurationCmd.value = "false"
        updateConfigurationCmd.scopename = "zone"
        updateConfigurationCmd.scopeid = 1
        self.apiClient.updateConfiguration(updateConfigurationCmd)

 

Marvinの実行

では、実際に結合テストを実行してみましょう。noseのテストランナーはnosetestsコマンドで、以下のようにオプションを指定して実行します。

nosetests --with-marvin --load --log-folder-path=<ログの出力ディレクトリ> --marvin-config=<Marvinの設定ファイル> <Marvinのテストコード>

すると、指定したログ出力ディレクトリ以下に、タイムスタンプが名前になったディレクトリが作成され、その下にfailed_plus_exceptions.txt、results.txt、runinfo.txtの3ファイルが作成されます。
テストの実行に成功すると、results.txtには以下のような出力がされ、失敗した場合は、failed_plus_exceptions.txtに失敗時のスタックトレースが出力されます。

test update configuration setting at zone level scope ... ok

----------------------------------------------------------------------
Ran 1 test in 0.147s

OK

VMのデプロイテスト

では次はVMのデプロイの結合テストを実行してみましょう。同じく、test/integration/smokeディレクトリ以下に、test_deploy_vm.pyというテストコードがあります。VMのデプロイを行い、そのレスポンスの検証を行ってくれるテストケースです。
一部、私の手元の物理サーバーで構築したCloudStack(KVM)環境だと動かなかった点があるので、注記すると、コンストラクタの中でテストデータを記述している部分で、デプロイに利用するテンプレートのostypeがCentOS 5.3 (64bit)となっていましたが、手元のCloudStack 4.3 RCの検証環境では、5.5のテンプレートが配備されているので、その点だけ修正する必要がありました。

 

    def __init__(self):
        self.testdata = {
            #data to create an account
            "account": {
                "email": "test@test.com",
                "firstname": "Test",
                "lastname": "User",
                "username": "test",
                "password": "password",
            },
            #data reqd for virtual machine creation
            "virtual_machine" : {
                "name" : "testvm",
                "displayname" : "Test VM",
            },
            #small service offering
            "service_offering": {
                "small": {
                    "name": "Small Instance",
                    "displaytext": "Small Instance",
                    "cpunumber": 1,
                    "cpuspeed": 100,
                    "memory": 256,
                },
            },
            "ostype": 'CentOS 5.5 (64-bit)',
        }

これを修正の上、同じく実行すると、

nosetests --with-marvin --marvin-config=../marvin/esxi.cfg --load test/integration/smoke/test_deploy_vm.py --log-folder-path=../marvin/

image
VMの作成が自動で行われ…

image
やがて立ち上がり、アサート処理が行われ、最終的に自動で削除されます。

テスト結果:

Test Deploy Virtual Machine ... ok

----------------------------------------------------------------------
Ran 1 test in 227.015s

OK

ちなみに、テストランナーの実行時に、テストファイルの指定を外してやると、そのディレクトリ以下のテストケースが全て実行されます。便利ですね。

nosetests --with-marvin --marvin-config=../marvin/esxi.cfg --load --log-folder-path=../marvin/

 

おわりに

以上、CloudStackの結合テストをMarvinで自動化する方法でした。ソフトウェアの開発は、バグとの戦いであり、バグを洗い出すためのテストの自動化は必須です。特に、CloudStackは、セットアップの一手順毎に時間のかかるリソース(VM、ストレージ)を管理するソフトウェアであることから、結合テストの各ケースの消化にとにかく時間が掛かるものであり(VMデプロイテストは自動化しても上の結果の通り、227秒掛かっています)、また複数のハイパバイザ、複数のストレージタイプ、ネットワークタイプをサポートしていることから、組み合わせは膨大な数に昇り、人手で実施しようとすると追いつきません。自動化することで、夜間バッチで実行したり、多数のマシンを並べて、並列実行させることも可能になります。今回は、既に構築済みのCloudStack環境に対して、Marvinでテストを実行する方法を解説しましたが、Marvinは、ゾーンやポッドの構成を設定ファイルに記述することで、テスト環境のセットアップの自動化も行う機能もあり、多数のテスト環境の構築の自動化用途にも最適です。

Marvinは、CloudStackの開発者にとって、自分の書いたコードが、ユーザーに対してリグレッションを発生させていないか確認する強力な手段です。OSSは、地理的に離れた様々な人が一つのコードベースをよってたかって開発する以上、テストのないコードは、壊されても文句は言えません。開発する際に、Marvinでの結合テストのコードも書いてコミットしておけば、今後別のCloudStackのコントリビュータがコードを弄った場合も、ユーザーに対してリグレッションが起きないことを担保出来ます。

そして同時に、MarvinはCloudStackのオペレータにとっても、強力なツールの一つになりえます。プロダクション環境のCloudStackに対して、デプロイやスナップショットのテストといった、ユーザー操作を模したテストを定期実行すれば、ユーザーからみたサービスの健全性の監視に使えます。ステージング環境に構築した新しいバージョンのCloudStackに対して自社の利用環境にあわせてカスタマイズしたテストスイートをMarvinで実行することは、CloudStack APIの受け入れテストの自動化に使えます。更に進めて、Communityで開発中のコードに対して自動化された結合テストを常に実行し、バグがあればコミュニティにフィードバックを行うようにすれば、開発段階の時点で、自社の利用環境で発生するリグレッションを潰すことが出来、サービスイン前の受け入れテスト不合格からくるプロジェクト遅延のリスクを抑えることが出来ます。

以上、今回のエントリが、CloudStack開発、そして結合テストを始めるにあたっての参考になれば幸いです。

Trackbacks : 4

Trackback URL for this entry
http://blog.sharplab.net/blog/2014/03/03/cloudstack%e9%96%8b%e7%99%ba%e3%81%93%e3%81%a8%e3%81%af%e3%81%98%e3%82%81-4%e7%b5%90%e5%90%88%e3%83%86%e3%82%b9%e3%83%88-mavin%e3%81%a7cloudstack%e3%81%ae%e7%b5%90%e5%90%88%e3%83%86%e3%82%b9/trackback/

Listed below are links to weblogs that reference this entry

トラックバック from ポーター porter 財布 小物 14-09-24 02:10:04 UTC

ポーター porter 財布 小物…

CloudStack開発ことはじめ (4)結合テスト -marvinでCloudStackの結合テストを自動化してみよう- – SharpLab….

トラックバック from Steps To Make A Woman Want You 15-02-15 15:50:25 UTC

Steps To Make A Woman Want You…

… – Women aren't attracted to a large bank account of be someone who knows what's best for you. For why man and woman attract each other, most guys is they don't wait… CloudStack開発ことはじめ (4)結合テスト –marvinでCloudStackの結合 … ……

トラックバック from Depressed Tumblr Theme Codes 15-05-05 16:23:33 UTC

Depressed Tumblr Theme Codes…

… – Hypertension and cholesterol medications can sometimes be caused by their developing body image. But it is – depression – helping others and being hopeful. Body aches and pains … CloudStack開発ことはじめ (4)結合テスト –marvinでCloudStackの結合 … ……

トラックバック from Mistra Group 16-06-02 10:48:11 UTC

Mistra Group

CloudStack開発ことはじめ (4)結合テスト –marvinでCloudStackの結合テストを自動化してみよう– – SharpLab.