ConoHaのサーバーにAPI経由でISOイメージをマウントする方法

ここではConoHaのサーバーにAPI経由でISOイメージをマウントする方法をご紹介します。
※この記事は2015年10月現在の情報をもとに記述しています。

##なぜAPIを使うのか
最も簡単な答えは、APIを使用しないと実現できない機能があるからです。今回の記事でもあるISOイメージの操作は、まさにそれにあたります。

##ISOイメージの使用手順
1. トークンの取得
1. ISOイメージのダウンロード
　(正確には「ダウンロード指示」というほうがよいかもしれません)

1. ISOイメージの一覧の取得
　(ダウンロードの確認、およびISOイメージpathの取得)
1. サーバー(VM)一覧の取得
　(ISOイメージのマウント先サーバーIDの取得)
1. ISOイメージのマウント（※VMが停止していること）
1. （VM上でISO使用）
1. ISOイメージのアンマウント（※VMが停止していること）

#####備考
- 正確を期すなら、APIのバージョン情報取得も実施した方が良いかもしれません
- ISOイメージの削除APIはないようです

##サンプルプログラム

```conoha_iso.py
#!/usr/bin/env python3
#  -*- coding: utf-8 -*-

"""conoha_iso.py

Usage:
  conoha_iso.py  downloadisoimage <url>
  conoha_iso.py  listisoimages
  conoha_iso.py  listservers
  conoha_iso.py  mountisoimage <server_id> <isoimage_path>
  conoha_iso.py  unmountisoimage <server_id>
  conoha_iso.py  -h | --help

List of Commands:
  downloadisoimage     download iso image from url
  listisoimages        list downloaded iso images
  listservers          list servers
  mountisoimage        mount iso image on the server
  unmountisoimage      unmount iso image from the server

Options:
  -h --help     Show this screen

"""

import sys
import json
import requests
from docopt import docopt

def checkStatus(res, normalCode=200):
    rc = (res.status_code == normalCode)
    if rc:
        print("Status OK:", end=" ")
    else:
        print("Status NG:", end=" ")
    print(res.status_code)
    return rc

def jsonDumpHeader(res):
    print(json.dumps(dict(res.headers), indent=2, separators=(',', ': ')))

def jsonDump(res):
    print(json.dumps(res.json(), indent=2, separators=(',', ': ')))

def getToken():
    url = "https://identity.tyo1.conoha.io/v2.0/tokens"
    headers = {"Accept": "application/json"}
    data = {"auth": {"passwordCredentials": {"username": username, "password": password}, "tenantId": tenant_id}}
    res = requests.post(url, data=json.dumps(data), headers=headers)
    if res.status_code != 200:
        print("getToken error: status=%d" % res.status_code)
        sys.exit(1)
    r = res.json()
    return r['access']['token']['id']

def downloadISOimages(tokenID, isoUrl):
    url = "https://compute.tyo1.conoha.io/v2/" + tenant_id + "/iso-images"
    headers = {"Content-Type": "application/json", "Accept": "application/json", "X-Auth-Token": tokenID}
    data = {"iso-image":{"url": isoUrl}}
    res = requests.post(url, data=json.dumps(data), headers=headers)
    checkStatus(res, 201)
    jsonDump(res)

def listISOimages(tokenID):
    url = "https://compute.tyo1.conoha.io/v2/" + tenant_id + "/iso-images"
    headers = {"Content-Type": "application/json", "Accept": "application/json", "X-Auth-Token": tokenID}
    res = requests.get(url, headers=headers)
    checkStatus(res)
    jsonDump(res)

def listServers(tokenID):
    url = "https://compute.tyo1.conoha.io/v2/" + tenant_id + "/servers"
    headers = {"Accept": "application/json", "X-Auth-Token": tokenID}
    res = requests.get(url, headers=headers)
    checkStatus(res)
    jsonDump(res)

def mountISO(tokenID, serverID, filePath):
    url = "https://compute.tyo1.conoha.io/v2/" + tenant_id + "/servers/" + serverID + "/action"
    headers = {"Accept": "application/json", "X-Auth-Token": tokenID}
    data = {"mountImage": filePath}
    res = requests.post(url, data=json.dumps(data), headers=headers)
    checkStatus(res, 204)

def unmountISO(tokenID, serverID):
    url = "https://compute.tyo1.conoha.io/v2/" + tenant_id + "/servers/" + serverID + "/action"
    headers = {"Accept": "application/json", "X-Auth-Token": tokenID}
    data = {"unmountImage": ""}
    res = requests.post(url, data=json.dumps(data), headers=headers)
    checkStatus(res, 204)

tenant_id = "XXXXX"
username = "XXXXX"
password = "XXXXX"

if __name__ == '__main__':
    args = docopt(__doc__)

    if args["downloadisoimage"]:
        downloadISOimages(getToken(), args["<url>"])
    elif args["listisoimages"]:
        listISOimages(getToken())
    elif args["listservers"]:
        listServers(getToken())
    elif args["mountisoimage"]:
        mountISO(getToken(), args["<server_id>"], args["<isoimage_path>"])
    elif args["unmountisoimage"]:
        unmountISO(getToken(), args["<server_id>"])
```

###使い方(準備編)
- プログラムはpython3で記述しています
- pythonに標準付属ではないモジュール"docopt"を使用しています。必要に応じてpipなどでインストールしてください
- Linux上で動かすことを想定しています（実際の開発はWindows上で行いましたが。。。）
- 上記プログラム中の　tenant_id = "XXXXX"　username = "XXXXX"　password = "XXXXX"　をご自身のものに書き換えてください
- プログラムは文字コードutf-8で保存してください
- 実行権限をあたえてください(chmod755などで)

###使い方(コマンド編)
    $ ./conoha_iso.py --help
でヘルプが表示されますので参考にしてください。

####使用例
LinuxMintをダウンロードする場合

    $ ./conoha_iso.py downloadisoimage "http://mirrors.kernel.org/linuxmint//stable/17.2/linuxmint-17.2-mate-nocodecs-64bit.iso"

結果(レスポンス)例

```
Status OK: 201
{
  "user": "XXxXXXe3XXXXX8xXXxX6609fXxX",
  "request": {
    "iso-image": {
      "url": "http://mirrors.kernel.org/linuxmint//stable/17.2/linuxmint-17.2-mate-nocodecs-64bit.iso"
    }
  }
}
```

この時点ではまだダウンロードが完了していない可能性が高いので次のコマンドで結果を確認する(ダウンロードが完了するまで何度か実行してみる)

    $ ./conoha_iso.py listisoimages

結果(レスポンス)例

```
Status OK: 200
{
  "iso-images": [
    {
      "path": "/mnt/isos/repos/tenant_iso_data/XXxXXXe3XXXXX8xXXxX6609fXxX/linuxmint-17.2-mate-nocodecs-64bit.iso",
      "size": 1669529600,
      "url": "http://mirrors.kernel.org/linuxmint//stable/17.2/linuxmint-17.2-mate-nocodecs-64bit.iso",
      "name": "linuxmint-17.2-mate-nocodecs-64bit.iso",
      "ctime": "Tue Jul 21 22:10:48 2015"
    }
  ]
}
```

続きは、最初に示した「ISOイメージの使用手順」に沿ってコマンドを実行してください
※トークンの取得については、特に気にする必要はありません（トークンには使用期限があるため、コマンド実行の都度取得しなおしています）

####備考
- このプログラムは「東京リージョン」固定としています
- コマンドのレスポンスは素直にJSON形式を多少読みやすく整形した程度にとどめています