Rubyから「さくらのクラウド」を操ろう 3ページ

さくらのクラウドAPI経由でサーバーのセットアップを行う

 さて、さくらのクラウドAPIを使ってサーバーの作成を行う例は「POSTリクエストでAPIを実行する」で紹介したが、これだけでは機能するサーバーを作成できない。実際には作成したサーバーにディスクを接続したり、ネットワークの設定を行う作業が必要となる。以下ではこれを行う作業手順を説明しておこう。

 この作業手順については前回も説明しているが、具体的には以下のようになる。

  1. サーバーを作成する
  2. ディスクを作成する
  3. サーバーにディスクを接続する
  4. サーバーにネットワークインターフェイスを作成する
  5. 作成したネットワークインターフェイスをルーターに接続する

 また、ディスク作成時には既存のディスクをコピーしたり、アーカイブからコピーするといったことも可能だ。ここではあらかじめさくらインターネットが用意しているアーカイブをコピーしてディスクを作成し、続いてディスクの内容(ホスト名およびパスワード)を変更する、という手順でディスクの作成を行うことにする。

リクエストを処理するためのクラスを作成する

 今回の例のように複数のリクエストを送信する場合、あらかじめリクエストを処理するためのクラスを作成しておくと便利だ。そこで今回は「sakura-cloud-request.rb」というファイル内に、以下のような「SakuraCloudRequest」というクラスを用意した。

# -*- coding: utf-8 -*-

require 'net/https'
require 'uri'

class SakuraCloudRequest
  def initialize(token, secret, zone='is1a', api_version='1.1')
    @token = token
    @secret = secret
    @zone = zone
    @api_version = api_version
    @base_url = "https://secure.sakura.ad.jp/cloud/zone/#{zone}/api/cloud/#{api_version}"
  end

  # リクエストの送信
  def send(method, path, params={})
    uri = URI.parse(@base_url + path)

    # HTTPSを使うための設定
    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    https.verify_mode = OpenSSL::SSL::VERIFY_NONE

    # リクエストオブジェクトの生成
    case method
    when 'GET'
      request_class = Net::HTTP::Get
    when 'POST'
      request_class = Net::HTTP::Post
    when 'DELETE'
      request_class = Net::HTTP::Delete
    when 'PUT'
      request_class = Net::HTTP::Put
    else
      raise ArgumentError, 'invalid method'
    end

    # BASIC認証のための設定
    req = request_class.new(uri.path)
    req.basic_auth(@token, @secret)

    # 送信するパラメータの設定
    if method == 'POST' || method == 'PUT'
      req.body=(JSON.generate(params))
    end

    # リクエスト送信
    res = https.start do |x|
      x.request(req)
    end

    return res
  end
end

 SakuraCloudRequestクラスでは、アクセストークンおよびアクセストークンシークレット、ゾーン、APIバージョンを指定してインスタンスを作成し、sendメソッドで指定したパスに対し指定したリクエストを送信できる。

 たとえば「GET /server」というリクエストを送信する場合、以下のようにすれば良い。

# アクセストークンおよびゾーン、APIバージョンを指定
token = '<アクセストークン>'
secret = '<アクセストークンシークレット>'
zone_id = 'is1a'
api_version = '1.1'

# リクエストオブジェクトを作成
req = SakuraCloudRequest.new(token, secret, zone_id, api_version)

# 「/server」にGETリクエストを送信する
  res = req.send('GET', '/server')

サーバーの作成

 さて、これらクラスを使ってサーバーおよびディスクの作成、ネットワーク関連の設定などを実行する手順は以下のようになる。

 まず、必要なパラメータを定義してSakuraCloudRequestクラスのインスタンスを作成する。

# -*- coding: utf-8 -*-

require 'json'
require './sakura-cloud-request'

# アクセストークンおよびゾーン、APIバージョンを指定
token = '<アクセストークン>'
secret = '<アクセストークンシークレット>'
zone_id = 'is1a'
api_version = '1.1'

new_password = 'FooBarHogeHoge' # 作成するサーバーに設定するパスワード
host_name = 'Test01' # 作成するサーバーのホスト名
server_name = 'Test01' # 作成するサーバーのサーバー名
disk_name = server_name + '_disk' # 作成するディスクのディスク名

## リクエストオブジェクトを作成
req = SakuraCloudRequest.new(token, secret, zone_id, api_version)

 続いて、サーバーの作成を行う。これは先に説明したとおり、/serverにPOSTリクエストを送信することで実行できる。

## サーバーを作成する
# 送信するパラメータ
params = {
  'Server' => { 
    'Name' => server_name,
    'Zone' => { 'ID' => 31001 }, # 石狩第1ゾーン
    'ServerPlan' => { 'ID' => 1001 }, # 1GB/1コアプラン
  }
}

# /server にPOSTリクエストを送信
puts 'create server...'
res = req.send('POST', '/server', params)

# 201以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '201'
  puts res.code + ': ' + res.message
  exit
end

# 取得したJSON形式のレスポンスをオブジェクトに変換
result = JSON.parse(res.body)

# 作成したサーバーのサーバーIDを取得
server_id = result["Server"]["ID"]
printf("server created. ID: %d\n", server_id)

ディスクの作成

 次に、ディスクの作成を行う。ディスクの作成は、/diskにPOSTリクエストを送信することで実行できる。プランIDについては「2」がHDD、「4」がSSDとなっている。これもドキュメントに明示はされていないが、「GET /product/disk」APIで確認できる。また、「SourceArchive」パラメータを指定することで指定したアーカイブからのコピーが可能となる。この場合、APIは成功時にはステータスコードとして201(Created)ではなく202(Accepted)が返ってくる点に注意したい。ディスクの複製には時間がかかるためだ。

## ディスクを作成
# 送信するパラメータ
params = {
  'Disk' => { 
    'Name' => disk_name,
    'Zone' => { 'ID' => 31001 },
    'Plan' => { 'ID' => 4 },
    'SourceArchive' => { 'ID' => 112500570421 }
  }
}

# 「/disk」にPOSTリクエストを送信
puts 'create disk...'
res = req.send('POST', '/disk', params)

# 202以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '202'
  puts res.code + ': ' + res.message
  exit
end

# 取得したJSON形式のレスポンスをオブジェクトに変換
result = JSON.parse(res.body)

# 作成したディスクのIDを取得
disk_id = result['Disk']['ID']
disk_status = result['Disk']['Availability']
printf("disk created. ID: %d\n", disk_id)

 ちなみに、アーカイブの一覧は「GET /archive」APIで取得できる。このAPIの出力結果を抜粋して以下に示すが、ここでは「CentOS 6.5 64bit (基本セット)」(ID:112500570421)を指定している。

{
  "From": 0,
  "Count": 29,
  "Total": 29,
  "Archives": [
    {
      "Index": 0,
      "ID": "112500513643",
      "DisplayOrder": "100100510011",
      "Name": "CentOS 5.10 64bit (基本セット)",
      "Description": "以下で入力したパスワードはrootユーザに設定されます。\nサーバ作成後、rootユーザでログインしてください。",
      "Scope": "shared",
      "Availability": "available",
      "SizeMB": 20480,
      "MigratedMB": 20480,
      "WaitingJobCount": null,
      "JobStatus": null,
      "OriginalArchive": {
        "ID": "112500513643"
      },
  
  
    {
      "Index": 1,
      "ID": "112500570421",
      "DisplayOrder": "100100605011",
      "Name": "CentOS 6.5 64bit (基本セット)",
      "Description": "以下で入力したパスワードはrootユーザに設定されます。\nサーバ作成後、rootユーザでログインしてください。",
  
  

 また、このようにしてアーカイブからのコピーを行った場合、コピーが完了するまでディスクに関する一部の操作が制限される。コピーの完了を検知するには、以下のように「GET /disk/:diskid」APIを実行してそのステータスをチェックすれば良い。

## ディスクの作成完了まで待機する
puts 'check disk status...'
until disk_status == 'available'
  # 10秒待機する
  sleep(10)
  # 「/disk/:diskid」にGETリクエストを送信してその情報を取得
  res = req.send('GET', '/disk/' + disk_id)
  # 202以外のHTTPステータスコードが返ってきたら終了する
  unless res.code == '200'
    puts res.code + ': ' + res.message
    exit
  end

  result = JSON.parse(res.body)
  disk_status = result['Disk']['Availability']
end
puts 'disk available.'

 ディスクのコピーが完了して利用可能になると、APIの実行結果のうち「Disk.Availability」要素が「’available’」となる。ここでは10秒おきにリクエストを送信し、ディスクが利用可能になるまで待機するようループ処理を行っている。

パスワードやホスト名などを設定する

 続いて、ディスクの内容を書き換えてパスワードやホスト名などの設定を行う。これはアーカイブからディスクを複製した場合にのみ利用できる機能で、「PUT /disk/:diskid/config」APIで実行できる。「:diskid」には対象とするディスクのディスクIDを指定する。

## ディスクの内容を書き換える
# 送信するパラメータ
params = {
  'Password' => new_password,
  'HostName' => host_name
}

# 「/disk/:diskid/config」にPUTリクエストを送信
puts 'modify disk...'
res = req.send('PUT', '/disk/' + disk_id + '/config', params)

# 200以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '200'
  puts res.code + ': ' + res.message
  exit
end

puts 'disk modified.'

サーバーにディスクを接続する

 作成したディスクは、サーバーへの接続処理を実行することでサーバーからアクセスが可能になる。ディスクの接続は「PUT /disk/:diskid/to/server/:serverid」APIで行える。「:diskid」および「:serverid」には、対象とするディスクのディスクIDと接続先のサーバーIDを指定する。

## サーバーにディスクを接続
# 「/disk/:diskid/to/server/:serverid」にPUTリクエストを送信する
puts 'connect disk to server...'
res = req.send('PUT', '/disk/' + disk_id + '/to/server/' + server_id)

# 200以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '200'
  puts res.code + ': ' + res.message
  exit
end

puts 'connected.'

ネットワークインターフェイスの作成

 ネットワークインターフェイスの作成は、「POST /interface」APIで実行できる。

## サーバーにネットワークインターフェイスを追加
# 送信するパラメータ
params = {
  'Interface' => {
    'Server' => {
      'ID' => server_id
    }
  }
}

# 「/interface」にPOSTリクエストを送信する
puts 'add interface...'
res = req.send('POST', '/interface', params)

# 201以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '201'
  puts res.code + ': ' + res.message
  exit
end

# 取得したJSON形式のレスポンスをオブジェクトに変換
result = JSON.parse(res.body)

# 作成したインターフェイスのIDを取得
interface_id = result["Interface"]["ID"]
printf("interface created. ID: %d\n", interface_id)

ネットワークインターフェイスをスイッチに接続

 作成したインターフェイスは、スイッチもしくは共有セグメントに接続して利用する。今回は共有セグメントに接続してインターネットに接続するよう設定した。「PUT /interface/:interfaceid/to/switch/shared」APIでこの処理が実行できる。

## インターフェイスを共有セグメントに接続
# 「/interface/:interfaceid/to/switch/shared」にPUTリクエストを送信する
puts 'connect interface to internet...'
res = req.send('PUT', '/interface/' + interface_id + '/to/switch/shared')

# 200以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '200'
  puts res.code + ': ' + res.message
  exit
end

puts 'connected.'

サーバーを起動する

 以上でサーバーの作成とセットアップはすべて完了だ。最後に「PUT/server/:serverid/power」APIを実行することで、サーバーを起動できる。

## サーバーを起動する
# 「/server/:serverid/power」にPUTリクエストを送信する
puts 'starting server...'
res = req.send('PUT', '/server/' + server_id + '/power')

# 200以外のHTTPステータスコードが返ってきたら終了する
unless res.code == '200'
  puts res.code + ': ' + res.message
  exit
end

puts 'server started.'

さくらのクラウドAPIを利用する際の注意点

 さくらのクラウドAPIを利用する際、注意したいのがAPIによって返ってくるHTTPステータスコードが異なる点だ。APIドキュメントには各コードの意味が解説されているが、どのAPIがどのコードを返すかは明示されていない。そのため、ステータスコードを見て成功か失敗かを判断する場合には十分に注意する必要がある。