CloudStack Archive

2014-05-28 12:33:56,782 DEBUG [kvm.storage.KVMStorageProcessor] (agentRequest-Handler-1:null) Copying template to primary storage, template format is qcow2
2014-05-28 12:33:56,798 DEBUG [kvm.storage.LibvirtStorageAdaptor] (agentRequest-Handler-1:null) copyPhysicalDisk: disk size:305463296, virtualsize:305459200 format:qcow2
2014-05-28 12:33:56,798 DEBUG [kvm.storage.LibvirtStorageAdaptor] (agentRequest-Handler-1:null) <volume>
<name>87d6232c-e681-11e3-b200-000c294946bd</name>
<capacity>305459200</capacity>
<target>
<format type='qcow2'/>
<permissions><mode>0744</mode></permissions></target>
</volume>

2014-05-28 12:33:56,848 DEBUG [utils.script.Script] (agentRequest-Handler-1:null) Executing: qemu-img info /mnt/7bd7a6e6-33b1-3314-a8e2-67d6de51e7a7/427295ce-95c1-4360-9c1d-c2de11a6cdca.qcow2
2014-05-28 12:33:56,859 DEBUG [utils.script.Script] (agentRequest-Handler-1:null) Exit value is 1
2014-05-28 12:33:56,860 DEBUG [utils.script.Script] (agentRequest-Handler-1:null) 'image' uses a qcow2 feature which is not supported by this qemu version: QCOW version 3Could not open '/mnt/7bd7a6e6-33b1-3314-a8e2-67d6de51e7a7/427295ce-95c1-4360-9c1d-c2de11a6cdca.qcow2': Operation not supported

ビルドした環境のqemu-imgのバージョンと、systemvmが稼動するKVM hostのqemuのバージョンがずれていると、qcow2イメージが読めずに起動しない場合があります。

APR
18

_images/acslogo.pngApache CloudStackのCommitterになりました。CloudStackのKVMサポート周りへパッチを投げてきたことが評価されたのでしょうか。CloudStackの開発への参加を通じて、OSS開発のワークフローや、大規模ソフトウェアのアーキテクチャ設計など、それぞれのメリット・デメリット含めて、多くのことを学ぶことが出来ました。まだまだ力不足感は否めませんが、これからも時間を見つけてApache CloudStackコミュニティへの貢献を続けることで、その発展の一助になれればと思います。

前回のエントリでは、Marvinを用いて、CloudStackの結合テストを実行する方法について解説しました。その際、テストの実行環境としてUbuntuを使用しましたが、普段自分はWindows上のIntelliJ IDEA Community Editionを使ってCloudStackのコードを開発している為、テストコードの開発、実行環境も出来ればWindowsに揃えたいところです。このエントリでは、Marvinのテストコード開発環境をPyCharmを使ってWindows上に構築した際の手順を解説します。

ツールのインストール

PyCharm

image

IntelliJ(のCommunity Edition)は、Java開発用のIDEですが、IntelliJから派生したPython開発用のIDEとして、PyCharmというものがあります。このPyCharm、IntelliJと同じく、Community Editionとしてフリー版が用意されており、今回はこれを利用してMarvinのテストを記述する方法を解説していきます。

http://www.jetbrains.com/pycharm/ からダウンロードできます。

Java

PyCharmの実行は、Javaが必要であり、また、後半の手順で、Marvinをコンパイルする際にも必要になるので、インストールしましょう。

http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html からダウンロードできます。

インストールが出来たら、忘れずにPATHを通しておきましょう。

MinGW

後半の手順で、MarvinをMavenでコンパイルする際に、bashが必要になります。MinGWなどをインストールしましょう。

http://www.mingw.org/ からダウンロードできます。

Python for Windows

PyCharmは、あくまでもIDEであり、Pythonの実行環境は提供しません。Python for windowsを別途インストールしましょう。

http://www.python.org/downloads/ からダウンロードできます。

インストールが出来たら、忘れずにPATHを通しておきましょう。

setuptools、pip

Pythonのパッケージ管理システムのsetuptools、pipをインストールしましょう。setuptoolsをセットアップするためのPythonスクリプトが配布されていますので、ダウンロードの上実行し、setuptoolsがパッケージを配置するC:\Python27\ScriptsにPATHを通した上でeasy_installコマンドからpipをインストールしましょう。

wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
python ez_setup.py

# ここでC:\Python27\ScriptsをPATHに設定

easy_install pip

Marvinが依存する外部ライブラリ

続いてはMarvinが依存する外部ライブラリです。mysql-connector-pythonと、pycryptoが必要ですので、それぞれインストールしましょう。

http://dev.mysql.com/downloads/connector/python/

http://www.voidspace.org.uk/python/modules.shtml#pycrypto

Marvin

そして本命のMarvinのインストールです。こちらは、Mavenでビルドしてパッケージを作成し、それを先ほどインストールしたpipでインストールするという流れになります。

まずは、CloudStackの開発に使用しているIntelliJで、MavinをMavenでビルドし、パッケージを作成しましょう。(IntelliJによるCloudStackの開発環境の構築は、CloudStack開発ことはじめ (1)開発環境のセットアップ –IntelliJ IDEA Community Editionのススメ –をご覧ください。)

image

作成されたパッケージは、tools/marvin/distディレクトリ以下に配置されます。

以下のように、pipコマンドでインストールを行いましょう。インストールを行うと、依存するパッケージ(nosetests)なども同時にインストールされます。

pip install tools/marvin/dist/Marvin-0.1.0.zip

以上で、一連のインストールは完了です。

PyCharmのセットアップ

プロジェクトの作成

続いて、PyCharmのセットアップを進めていきましょう。まずは、CloudStackのPyCharmプロジェクトの作成です。IntelliJでCloudStackの開発をする際に使用しているプロジェクトをそのまま使いたいところですが、使用しようとするとプロジェクトファイルが競合してしまいます。仕方がないので、別のディレクトリにレポジトリをクローンしてそのレポジトリをインポートする形で別のプロジェクトとして開発を進めましょう。

git clone https://github.com/apache/cloudstack.git cloudstack_py

プロジェクトのテストランナーの変更

CloudStackの結合テストは、nosetestsで書かれていますので、PyCharmで結合テストを実行するには、プロジェクトのテストランナー設定を変更する必要があります。
ProjectのSettingsダイアログを開き、Python Integrated Tools->Default test runnerと項目を辿り、”Notestests”に設定しましょう。

image

テストコード実行時のパラメータの変更

続いて、nosetestsのオプションのデフォルトパラメータの変更を行い、nosetestsにmarvinプラグイン、marvinのconfigが読み込まれるようにしましょう。

--with-marvin --marvin-config=<MarvinのConfigへのパス> --load

 

テストの実行

テストの実行は、テストコードのコンテキストメニューから開始できます。
image

 

PyCharmに統合されたテストランナーの表示

テスト結果は、PyCharmに統合されたテストランナーが進捗含めわかりやすく表示してくれます。
image

PyCharmでのデバッグ実行

続いては、デバッグ実行です。ブレークポイントを打ち、デバッグ実行することで、コードの実行を任意の箇所で止めながらデバッグすることが可能です。

image
勿論、操作感は、IntelliJやRubyMineと同じです。その場で書いたコードを現在のコンテキストで評価する、Expression Evaluationウィンドウも健在です。いいですね。

以上、PyCharmでMarvinによるCloudStackのテストコードをデバッグする方法の紹介でした。今回のエントリが、テストコード開発を始めるきっかけになれば幸いです。

 

 

なお、自分は触ったことはないですが、Windows上で動作するPython IDEのもう一つの選択肢として、Python Tools for Visual StudioというIDEもあります。Visual Studioというとお高そうなイメージがありますが、こちらは無償です。PyCharmと比較すると、nosetestsのテストランナーが統合されてない点は劣りますが、一方でPyCharm Community Editionでは提供されていない、リモートデバッグ機能があるなど、優れた点もあります。こちらも、検討してみると面白いかもしれません。
image

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開発、そして結合テストを始めるにあたっての参考になれば幸いです。

FEB
6

Management Serverをセットアップする前に、vhd-utilをManagement Serverに配置しておく必要があります。

参考:https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.0.2/html/Installation_Guide/management-server-install-flow.html

ライセンス上の問題から、一緒に配布出来ないための措置の模様。面倒ですね。。あまりマニュアル読まずに進めてたら嵌りました。。

このエントリは、CloudStack Advent Calendarの12月17日分のエントリです。

さて、12/4のエントリでは、CloudStackをIntelliJ IDEAでビルドする方法を説明しました12/6のエントリでは、JenkinsでCloudStackのdebパッケージをビルドする方法を説明しました。今日はそのdebパッケージを使ってCloudStackを実際に構築したとして、その環境をIntelliJ IDEAからリモートデバッグをする方法を解説します。

Debuggee(CloudStack)の設定

CloudStackはJavaのプログラムですが、Javaは起動時のオプションで、リモートデバッグの受付を設定出来ます。Management Serverの場合はTomcatの設定ファイル(/etc/cloudstack/management/tomcat6.conf)、KVM Host Agentの場合は起動スクリプト(/etc/init.d/cloudstack-agent)内で起動オプションを設定変更できるので、例えば以下のように変更を加えると、8787番ポートでデバッガのアタッチを待ち受けるようになります。

sed -i 's/JAVA_OPTS="/JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n /g' /etc/cloudstack/management/tomcat6.conf
sed -i 's/$JSVC -cp/$JSVC -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -cp/g' /etc/init.d/cloudstack-agent

なお、上記の例ではsuspendオプションにnを渡していますが、yを渡すと、デバッガがアタッチされるまで実行が抑止されます。このオプションは、起動してすぐ、デバッガをアタッチする間もなく実行されてしまう箇所のデバッグをするときに有効です。yを渡しておくと、デバッガがアタッチされるまで実行が抑止されることでその箇所のデバッグが可能になります。

Debugger(IntelliJ IDEA)の設定

“Run”メニューの”Edit configuration”メニューから、“Run/Debug Configuration”ウィンドウを開きます。

すると、デバッグ対象を左上の"+"ボタンから追加できるので、"Remote"を選択して追加し、NameとHost、Portを対象のサーバーにあわせて適宜編集してください。

image

これを対象のサーバー分だけ繰り返すと、Debugボタンを押したときにデバッグ対象を選択してリモートデバッグを行えるようになります。

image

当然、ソースコード中にブレークポイントを置いてコードの実行を一時停止させたり、

image

Intelli J IDEAの"Expression Evaluation"ウィンドウという、Visual Studioで言うところの"Immidiate Window"的な、入力した式を現在のスコープで評価するツールを使ったりすることも出来ます。

image

ちょっと短めですが、以上、CloudStackをIntelliJ IDEAでリモートデバッグするまでの手順の紹介でした。CloudStackは、比較的OSSとしてはドキュメントが充実している方ですが、それでも開発が非常にアクティブなこともあり、ドキュメント化されていない仕様や、周知が十分でない仕様変更が多くあります。CloudStackの開発にこれから取り組もうという方は勿論、CloudStackを導入するにあたって、仕様を深く理解しようとコードを追う人にとっても、この記事が一つの参考になれば幸いです。

このエントリは、CloudStack Advent Calendar 12月6日分のエントリです。

前回のエントリに引き続きCloudStackの開発方法について書いていきます。前回のエントリではIntelliJ IDEAを使って、CloudStackのコードを手元でビルド出来るところまでセットアップする方法を紹介しました。ただビルドするだけではなく、パッケージとして纏めるところまで出来たら、自分でコードの変更を加えたCloudStackをインストールしてテストするときに便利だよね、ということで、今日はamd64版向けのdebパッケージを自分でビルドしてみましょう。そして、debパッケージをビルドするだけではなく、今回はJenkinsでビルドサーバーを立てて、Jenkinsでdebパッケージをビルドしてみることを目指します。

Jenkinサーバーのセットアップ

debパッケージをビルドするため、前回のエントリで使っていたWindows+IntelliJ IDEA環境から離れて、今日はUbuntu ServerにJenkinsをインストールしてみましょう。今回は12.04 LTSを使用しました。

Javaの開発環境のインストール

まずはJavaの開発環境のセットアップから。Oracle JDKをインストールしていきます。標準ではOracle JDKは入らないので、ppaを使用して、以下のようにインストールします。

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java7-installer
sudo bash -c "echo \"JAVA_HOME=/usr/lib/jvm/java-7-oracle/\" >> /etc/profile" 
sudo bash -c "echo \"export JAVA_HOME" >> /etc/profile"
sudo bash -c "echo \"JAVA_HOME=/usr/lib/jvm/java-7-oracle/\" >> /etc/default/tomcat6" 

add-apt-repositoryが見つからないと起こられる場合は、以下のコマンドでインストールできます。あるいは自分でレポジトリを追加してもよいでしょう。

sudo apt-get install python-software-properties

つづいてMavenのインストール

sudo apt-get install maven

状況確認。ちゃんとインストールされてますね。

mvn -v                                                                                                       
Apache Maven 3.0.4
Maven home: /usr/share/maven
Java version: 1.7.0_45, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-7-oracle/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "3.8.0-34-generic", arch: "amd64", family: "unix"

パッケージビルド用のツールのインストール

パッケージビルド時に必要となるツールを、以下のコマンドでインストールしましょう。

sudo apt-get install debhelper tomcat6 genisoimage python-mysqldb

Jenkinsのインストール

そしてJenkinsを以下のコマンドでインストールしましょう。今回はパッケージからインストールを行います。

sudo apt-get install jenkins-tomcat

では、JenkinsのWeb UIにアクセスしてみましょう。デフォルトで、ポート8080番で立ち上がっています。

image

本当はここでちゃんとユーザーや認証の設定などをするべきなのですが、今回はCloudStackをビルドすることに絞って説明を行います。

まずは、「Jenkinsの管理」->「システムの設定」からMavenを追加します。3.0.4をパッケージからインストールしてあるので、そのパスをMAVEN_HOME欄に設定します。

image

続いて、プラグインの追加画面から、Git Pluginの追加を行います。プラグインの追加が終わったら、有効化するために、Jenkinsを再起動する必要があります。

image

debパッケージのビルド

image

さて、いよいよパッケージのビルドをするために、Jenkinsのジョブのセットアップを行いましょう。Jenkinsのトップから、「新規ジョブ作成」を選択し、適宜名前をつけてジョブを作成してください。

image

ジョブの設定画面では、ソースコード管理システムはGitを選択し、Repository URLには使用しているレポジトリのURLを入力してください。
ビルドの項は、「ビルド手順の追加」から「シェルの実行」を選択し、以下の内容を貼り付けてください。

cd $WORKSPACE
VERSION=`(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version) | grep '^[0-9]\.'`
rm -rf dists
mkdir -p $WORKSPACE/dists/debian
tar --transform s,^\./,cloudstack-${VERSION}/, -c -z -f dists/debian/cloudstack-${VERSION}.tgz --exclude .git --exclude dists .
cd $WORKSPACE/dists/debian
tar -xzf cloudstack-${VERSION}.tgz
cd $WORKSPACE/dists/debian/cloudstack-${VERSION}
dpkg-buildpackage -uc -us
cd $WORKSPACE/dists/debian
mkdir -p $WORKSPACE/dists/debian/main/binary-amd64
mv *.deb *.changes *.dsc main/binary-amd64
cd $WORKSPACE
dpkg-scanpackages dists/debian/main/binary-amd64 /dev/null | tee $WORKSPACE/dists/debian/main/binary-amd64/Packages | gzip -9 > $WORKSPACE/dists/debian/main/binary-amd64/Packages.gz

以上で、Jenkinsのジョブのセットアップは完了しました。これでジョブを実行すれば、以下のようにビルドに成功する筈です。

image

そして、ワークスペースのdists/debian/main/binary-amd64/ディレクトリ配下には、debパッケージとPackagesファイルが生成されている筈です。

image

このdebパッケージを使えば、ビルドされたCloudStackをDebian/Ubuntu環境にインストールすることが出来ます。また、同時に生成されたPackagesファイルは、そのディレクトリ配下にあるパッケージの情報を含んだメタデータファイルであり、aptコマンドがそのディレクトリをレポジトリとして扱うことを可能にします。Jenkinsはワークスペースの内容をデフォルトでインターネットに対して公開してくれるので、そのURLを対象のサーバーにレポジトリとして登録すれば、Jenkinsサーバーをレポジトリとして使用することが出来ます。

sudo add-apt-repocitory 'http://<domain>:8080/jenkins/job/<jobname>/ws/dists/debian main'

CloudStackの実際のインストール手順については、CloudStack Installation Guideを参照されると良いかと思います。

以上、CloudStackのdebパッケージをビルドするためのJenkinsジョブセットアップ手順でした。明日のAdvent Calenderの担当は・・・誰なんだろう。。

image

このエントリは、CloudStack Advent Calender 12/4のエントリです。

 

みなさん、CloudStack開発してますか?
まだ始めていない方には、きっとこの記事は良い導入になるかと思います。
既に始めている方は、お使いのIDEは何ですか?Eclipse?それともVim/Emacs?
Java開発で敢えてVim/Emacsをお使いの方々は、それぞれ譲れない宗教をお持ちでしょうからいいですが、 Eclipseで開発している方は、CloudStackだとm2eのエラーが出て、なんとかならないかな、とか感じていませんか?
残念ながら、Eclipseを使い続ける限り、今のところ回避策はないようです。
エラーが出続ける状態で開発を行うのは心の健康にも、また実際の開発上も他のエラーを見落とす可能性があることから良くありません。 幸い、JavaのIDEには、軽量でモダンなUIを持ちながら、十分な機能を予め備えたIntelliJ IDEAというIDEが存在します。
今回はIntelliJ IDEAでCloudStack開発を行うためのセットアップ方法を紹介します。なお、今回はWindows上での手順を解説しますが、IntelliJ IDEAはマルチプラットフォーム対応なので、適宜読み替えることでMacやLinuxでも動くかと思います。

IntelliJ IDEAとは?

IntelliJ IDEAとは、JetBrains社が開発しているIDEで、Javaを中心にRubyやPython、PHPの開発にも対応した高機能IDEです。
JetBrains社は、他に、RubyMineやPyCharmというIDEをリリースしていますが、これらは、IntelliJ IDEAからRuby開発やPython開発に関わる部分を切り出して単体IDEとしてまとめたものになります。
そして、Java開発に機能を絞ったものとしてIntelliJ IDEA Community Editionというバージョンがあり、これはOSSとして配布されています。
今回はこのCommunity Editionを使っていきます。

Javaのセットアップ

IntelliJは、Javaで記述されたIDEであり、Javaの実行環境を必要とします。また、CloudStackのコンパイルを行うときにも必要になるので、まだインストールしていない場合は、JDKをインストールしましょう。

Oracle JDKは以下のサイトからダウンロードできます。

http://www.oracle.com/technetwork/java/javase/downloads/index.html

image

gitのセットアップ

CloudStackのソースコードは、gitで管理されています。git for windowsでgitをインストールしてしまいましょう。

http://www.git-scm.com/

image

Mavenのセットアップ

CloudStackは、ビルドシステムとしてMavenを使用しています。IntelliJはMaven連携機能がありますが、Mavenのバイナリは別に用意する必要があるので、以下のサイトからダウンロードの上、適宜展開しておきましょう。

http://maven.apache.org/

IntelliJのセットアップ

先述のとおり、IntelliJには、OSSであるCommunity Editionと、Ruby開発やPython開発、その他多くの機能強化がされたUltimate Editionがあります。とりあえずはCommunity Editionで十分かと思いますが、Ultimate Editionにはオープンソース開発者向けのライセンスもあるので、Apache CloudStackのコミッタになって、ライセンスを狙うのもありかもしれません。

image

IntelliJは以下のサイトからダウンロード出来ます。

http://www.jetbrains.com/idea/download/

image

IntelliJでCloudStackのプロジェクトの作成

image

IntelliJの初回起動時は上のようなダイアログが開きます。この中から、”Checkout from Version Control”を使用して、CloudStackをレポジトリからcloneしましょう。

Apache CloudStackのCommunityのレポジトリのURLは

https://git-wip-us.apache.org/repos/asf/cloudstack.git

になります。結構レポジトリのサイズが大きいので、多少時間が掛かっても驚かないようにしましょう。

image

なお、gitコマンドを直接使用してcloneしてきたディレクトリが既にある場合は、Import Projectオプションを使用して、プロジェクトを作成することも出来ます。

IntelliJの設定変更

Appearance

IntelliJは設定画面の”Appearance”->”Theme”の項から、IDEとしての外観を変更することが出来ます。趣味の問題ですが、個人的には”Darcula”がお勧めです。

image

Allow placement of caret after the end of line

IntelliJでは、デフォルトで、”Editor”->”Allow placement of caret after the end of line”というオプションがオンになっており、これはキャレットを行のどこにでも置いてそこから編集を始められるという機能なのですが、あまり使いどころがなく、むしろ邪魔になることが多い機能なのでオフにするのをお勧めします。なお、次のVersionではデフォルトでオフになると聞いています。

image

IntelliJへのMavenの設定

Mavenのセットアップの項でダウンロードして展開したMavenのパスをIntelliJに設定します。

IntelliJの設定で、”Maven”->”Maven home directory”か、環境変数”M2_HOME”のどちらかで設定することが出来ます。

image

プロジェクトで使用するJDKの設定

続いて、プロジェクトで使用するJDKの設定を行いましょう。”File”->”Project Structure”のメニューから、Project Structure画面を呼び出し、その中の”Project”->”Project SDK”を使用して、使用するJDKのパスをセットしてください。

image

IntelliJのMaven連携を使用したCloudStackのビルド

以上の設定によって、IntelliJでCloudStackをコンパイルする用意が出来ました。では実際に、CloudStackをMavenでビルドしてみましょう。

InteliJには右側にMavenというペインがあり、それを開くと、Mavenのプロジェクト一覧が表示されます。その中で一番上にある、CloudStackプロジェクトを選択し、”Run Maven Build”を実行すると、Mavenによるビルドが始まります。初回は、Mavenによる必要な依存パッケージのインターネットからの取得などで結構時間が掛かるかと思いますが、最終的に以下のようなメッセージが出れば、ビルド成功です。

image

 

 

…と、ここまでエントリ書いたところで、IntelliJ IDEA 13がちょうど今日リリースされました!なんというタイミングの良さ!(悪さ?)IntelliJ IDEA 12で以上説明してきましたが、多分IntelliJ IDEA 13でも同じ手順でセットアップできるかと思います。CloudStack開発を始めるうえで、参考になれば幸いです。

image

CloudStackをご存知でしょうか。CloudStackは、オープンソースで開発が進んでいるIaaS基盤のソフトウェアです。もとはCloud.comというベンチャーが開発を進めていたのですが、CitrixがCloud.comを買収し、Apacheライセンスに変更の上Apache Foundationにソースコードを寄贈したことで、今はApache CloudStackとしてコミュニティを中心にCitrixが支援する形で開発が進んでいます。

現在、会社の業務の一環として、Apache CloudStackへのコードでの貢献に一部の時間を割いており、これまでいくつかのBugfixと、KVM上に構築されたCloudStackのゲストネットワークをVXLAN対応させるコードの投稿を行ってきました。

VXLAN対応の開発は、前任の@haeenaさんが行った開発の引継ぎなのですが、このVXLANという技術、VLANのVLAN ID枯渇問題(VLANはVLAN IDのフィールド長が12bitしかないため、複数ゲストネットワークをマルチテナントでIaaSとして提供しようとするような環境ではVLAN IDが足りなくなる)に対応するために、IETFで策定中のプロトコルです。L2のオーバーレイネットワークをIPネットワーク上に実現するため、L2フレームをUDPデータグラムでカプセル化し、ユニキャストフレームはIPユニキャスト、ブロードキャストフレームはIPマルチキャストで転送するというシンプルな仕様であり、そこが魅力ですね。Linuxでは、Kernel 3.7以降でVXLANを扱うための実装が入っており、CloudStackのVXLAN対応は、VLANの仮想インターフェイスを作成する代わりにVXLANの仮想インターフェイスを作成するように変更することで実現されています。

 

さて、そのVXLAN対応の開発について、先日11/20-22の日程でAmsterdamで開催された、CloudStack Collaboration Conference November 2013喋ってきました。Hackathonやセッション後の機会に、Hugo TrippaersやAlex HuangといったCloudStackのトップ開発者と話す機会がありましたが、いずれも建設的なコメントを貰えたのはありがたい限りです。また、聴講したパネルセッションなどを通じて、CloudStackの開発者が抱いているCloudStackの開発プロセスに対しての課題認識(masterへのマージ前にテストやコンパイルを行うようなゲートチェックが行われていないためにmasterがビルドすら通らなくなるなど品質が悪い等)が共有されていることを知ることが出来たのも収穫でした。来年はCloudStack Dayというイベントを日本でやるそうですが、こういったオフラインのイベントも、大切にしていきたいですね。

※アニメーションやビデオを使用しているので、PowerPointで開きなおすとわかりやすいかと思います。