CloudStack Advent Calender 12/3
読了時間: 多分5~6分くらい
間が空き過ぎてアレな感じですが勢いで CloudStack Advent Calender に手をあげてしまったので、書き殴っておきます。 ※別に Cloudstack に詳しくも何ともありません。
Fog とは、最近流行りの「クラウドサービス」や、その基盤として利用されているパッケージのAPIを、Rubyから気軽に利用できるライブラリ群です。その適用範囲は非常に多岐に渡り、今回の記事の対象となる CloudStack は元より、Amazon Web Services の EC2 や S3 、 joyent 、 Rackspace 、 VMware vSphere 、 Xen Server など、様々なパッケージに対応しています。
Fog のソースコードは以下にあります。
https://github.com/fog/fog
早速ですが、Fog を使いながらそのコードを追う事で、何となく Fog を理解した気になってみましょう。今回は対象となる CloudStack、Rubyやbundlerなど基本的な環境は揃っている物とします。 (Ruby1.9.3、bundlerがあればとりあえず動きます)
今回やる事は以下の通りです。
- 環境の準備
- サンプルコードの作成と、インタフェースの理解
- ソースコードの流し読み
1.環境の準備
mkdir fogtest
cd fogtest
bundle init
echo ' gem "fog","1.8.0" ' >> Gemfile
bundle install --path vendor/bundle
ls -al
これだけで周辺環境の整理は終わりです。後は FogのAPIを呼び出すためのコードを書けば終了です。シェルスクリプト臭い記述ですが気にせず Ruby コードを書きます。
2. サンプルコードの作成とインタフェースの理解
Rubyのサンプルコード list_service_info.rb
require 'rubygems'
require 'fog'
require 'pp'
ACCESS_KEY_ID='<アクセスキー>'
SECRET_ACCESS_KEY='<シークレットキー>'
END_POINT='<システムのエンドポイントURI>'
# create a compute connection
cloudstack_uri = URI.parse(END_POINT)
@compute = Fog::Compute.new(
:provider => 'CloudStack',
:cloudstack_api_key => ACCESS_KEY_ID,
:cloudstack_secret_access_key => SECRET_ACCESS_KEY,
:cloudstack_host => cloudstack_uri.host,
:cloudstack_port => cloudstack_uri.port,
:cloudstack_path => cloudstack_uri.path,
:cloudstack_scheme => cloudstack_uri.scheme,
)
# compute operations go here
pp @compute.list_zones
pp @compute.list_templates({'templateFilter' => 'executable'})
pp @compute.list_service_offerings
中核になるのは Fog::Compute.new であるのは一目瞭然ですね。
END_POINT は例えば以下のような値になります。自分の立てた CloudStackの環境に合わせてください。
http://<your management server>:8080/client/api
Cloudstack を立てるより外部サイトで試したいなら、Cloudn のアカウントを取得しておもちゃ代わりに使ってみるのもありです (ステマ)
END_POINT=’https://mycloud3.securesites.com/client/api’
では軽く叩いて動作を確かめましょう。
bundle exec ruby list_service_info.rb
ここで読み出した list_zones やliste_templates 、 list_service_offeringsに注目しましょう。これらは裏側で該当する CloudStack API を叩いてその基盤に用意されているゾーンやテンプレートの情報、インスタンスサイズの情報を読み出しています。この値を利用する事で、VMの作成に必要な情報を取得する事が出来ます。
これらの返り値は例えば以下のような形式です。
返り値のサンプル (list_templates)
{"listtemplatesresponse"=>
{"count"=>1,
"template"=>
[{"id"=>"**********************",
"name"=>"Hoge VM,
"displaytext"=>"Hoge VM for User",
"ispublic"=>false,
"created"=>"2012-08-21T17:57:06+0900",
"isready"=>true,
"passwordenabled"=>true,
"format"=>"QCOW2",
"isfeatured"=>false,
"crossZones"=>false,
"ostypeid"=>"Fuga",
"ostypename"=>"Fuga64bit",
"account"=>"SampleUser",
"zoneid"=>"Foo",
"zonename"=>"Bar",
"status"=>"Download Complete",
"size"=>XXXXXXXXX,
"templatetype"=>"USER",
"hypervisor"=>"KVM",
"domain"=>"SampleDomain",
"domainid"=>"XXXXXXXXXXXXXXXX",
"isextractable"=>true,
"checksum"=>"XXXXXXXXXXXXXXXX"}]}}
template がどのような情報を持っているのか、簡単に分かりますね。
では、受け取った情報を元に、VMを作成してみます。
Rubyのサンプルコード deploy_vm.rb
require 'rubygems'
require 'fog'
require 'pp'
ACCESS_KEY_ID='<アクセスキー>'
SECRET_ACCESS_KEY='<シークレットキー>'
END_POINT='<システムのエンドポイントURI>'
# create a compute connection
cloudstack_uri = URI.parse(END_POINT)
@compute = Fog::Compute.new(
:provider => 'CloudStack',
:cloudstack_api_key => ACCESS_KEY_ID,
:cloudstack_secret_access_key => SECRET_ACCESS_KEY,
:cloudstack_host => cloudstack_uri.host,
:cloudstack_port => cloudstack_uri.port,
:cloudstack_path => cloudstack_uri.path,
:cloudstack_scheme => cloudstack_uri.scheme,
)
# compute operations go here
pp @compute.deploy_virtual_machine({
'name' => 'fogtestvm',
'serviceOfferingId' => '<インスタンスサイズ>',
'templateId' => 'テンプレートID',
'zoneId' => 'ゾーンID',
})
pp @compute.list_virtual_machines
<インスタンスサイズ>: list_service_offerings の response にある name を元に id を決める ※有償サービスを利用している場合は、お金が掛かるのでサイズの選択ミスに注意
<テンプレートID>:list_templates の response にある templatename を元に templateid を決める
<ゾーンID>: list_zones の response にある zonename を元に zoneid を決める
bundle exec ruby deploy_vm.rb
list_virtual_machinesの結果を眺めると、 status 情報等も一緒に取れている事が分かります。deploy_virtual_machine の返り値を李ようしてVMを特定する事で、特定のVMのjobの状況などを追う事も出来ます。
3.ソースコードの流し読み
さて、基本は上記のようにせこせこと接続を作ってはAPIに各種命令を放り込めばいいんじゃないかなと言う感じですが、この辺Fogでは裏側がどうなっているのかソースコードを軽く追ってみます。
bundle exec gem env
の GEM PATHSの下に gems/fog-1.8.0/ がありますので、そちらに移動します。
最初に サンプルコードで require ‘fog’ を入力する事で、gemが走査対象に入ります。次にFog::Compute で newを呼び出すと、fog/compute.rb の Computeモジュールの new メソッドに各種設定情報を入れたハッシュを引数として渡します。この値は例えば 通信相手が AWS なのか Cloudstack なのかを特定する provider や、クラウドスタックのアクセスホストを示す cloudstack_host が該当します。
次に、 newで受け取ったオプションからprovider を元に呼び出すComputeサービス(今回の場合は Fog::Compute::Cloudstack)を特定し、関連ファイルを requireします。
lib/fog/compute.rb
8 def self.new(attributes)
9 attributes = attributes.dup # prevent delete from having side effects
10 provider = attributes.delete(:provider).to_s.downcase.to_sym
11
12 case provider
13 when :aws
14 require 'fog/aws/compute'
23 require 'fog/cloudstack/compute'
24 Fog::Compute::Cloudstack.new(attributes)
provider自身をdeleteメソッドで取り除いた残りのオプションを、Fog::Compute::CloudStackに引数で渡して CloudStack のオブジェクトを生成します。
Fog::Compute::CloudStack クラスは Fog:: Service ( lib/fog/core/service.rb )を継承しており、そちらで Option を評価したりした後 Cloudstackが呼び出す service::Realを指定しています。(この辺流し読みなのでちょっと合ってるか怪しい)
lib/fog/core/service.rb
55 def new(options={})
56 options = Fog.symbolize_credentials(options)
57 options = fetch_credentials(options).merge(options)
58 validate_options(options)
59 coerce_options(options)
60 setup_requirements
61
62 if Fog.mocking?
63 service::Mock.send(:include, service::Collections)
64 service::Mock.new(options)
65 else
66 service::Real.send(:include, service::Collections)
67 service::Real.send(:include, service::NoLeakInspector)
68 service::Real.new(options)
直接呼び出された lib/fog/cloudstack/compute.rb に戻って initialize コンストラクタを見ると、ここでようやく受け取ったオプションから connection を生成している事が分かります。
lib/fog/cloudstack/compute.rb
136 class Real
137
138 def initialize(options={})
139 @cloudstack_api_key = options[:cloudstack_api_key]
140 @cloudstack_secret_access_key = options[:cloudstack_secret_access_key]
141 @cloudstack_session_id = options[:cloudstack_session_id]
142 @cloudstack_session_key = options[:cloudstack_session_key]
143 @host = options[:cloudstack_host]
144 @path = options[:cloudstack_path] || '/client/api'
145 @port = options[:cloudstack_port] || 443
146 @scheme = options[:cloudstack_scheme] || 'https'
147 @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", options[:cloudstack_persistent], {:ssl_verify_peer => false})
148 end
この実態は呼び出し先の connection を見れば分かる通り、 excon (EXtended http(s) CONnections)
の gem です。
lib/fog/core/connection.rb
4 def initialize(url, persistent=false, params={})
5 Excon.defaults[:headers]['User-Agent'] ||= "fog/#{Fog::VERSION}"
6 @excon = Excon.new(url, params)
bundle exec gem list
ノリで始めて適当に見ているので、読み間違いがあったらアレな感じですが、大体こんな感じでサービスの選択がされているようです。ちょっと長くなってきたので、今回はここまでにします。