EC2インスタンスの価格表を生成するPythonスクリプトを作成しました

EC2の価格表を生成する必要があったので、その際のコードを公開します。以下をPythonで実装した感じです。

https://dev.classmethod.jp/cloud/was-price-list-one-liner/

実行環境

  • Python 2.7.10

Pythonスクリプト

東京リージョンのオンデマンド価格のみ必要であったため、それ以外の情報は破棄しています。

# coding: utf-8

import json
import re
import sys
import datetime
import urllib2

def __get_price_list(url):
    response = urllib2.urlopen(url)
    # 最終行のcallbackしているJavaScript行を取得する
    callback_line = response.readlines()[-1]
    # callback処理の文字列を除去する
    js_string = callback_line[len('callback('):-len(');')]
    # JSONに変換するためkey部分に""を付ける
    p = re.compile(r'([0-9a-zA-Z]*):')
    json_string = p.sub((lambda m:'"' + m.group(1) + '":'), js_string)
    price_json = json.loads(json_string)

    # key: region:operating_system:instance_type, value: price のdictに変換する
    prices = {}
    for price_per_region in price_json['config']['regions']:
        region = price_per_region['region']
        if region != 'ap-northeast-1':
            continue

        for instance_family in price_per_region['instanceTypes']:
            for instance_size in instance_family['sizes']:
                instance_type = instance_size['size']
                for value_column in instance_size['valueColumns']:
                    operating_system = value_column['name']
                    price = value_column['prices']['USD']
                    # priceが N/A の場合はリストに追加しない
                    if price == 'N/A':
                        continue

                    prices[region + ':' + operating_system + ':' + instance_type] = price

    return prices


def main(argv):
    price_list = {
        'created': str(datetime.datetime.now()),
        'prices': {}
    }
    prices = price_list['prices']
    # 旧世代
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/linux-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/rhel-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/sles-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswin-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswinSQL-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswinSQLWeb-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswinSQLEnterprise-od.min.js'))

    # 現行世代
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/rhel-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/sles-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/mswinSQL-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/mswinSQLWeb-od.min.js'))
    prices.update(__get_price_list('http://a0.awsstatic.com/pricing/1/ec2/mswinSQLEnterprise-od.min.js'))

    print json.dumps(price_list, sort_keys=True, indent=4)


if __name__ == "__main__":
    main(sys.argv)

出力結果は以下のようになります。diffが取りやすいようにKeyでソートしてあります。

{
    "created": "2017-07-11 20:11:55.347102", 
    "prices": {
        "ap-northeast-1:linux:c1.medium": "0.158", 
        "ap-northeast-1:linux:c1.xlarge": "0.632", 
...
        "ap-northeast-1:sles:x1.16xlarge": "9.771", 
        "ap-northeast-1:sles:x1.32xlarge": "19.441"
    }
}

Price List APIを使わないのは?

ドキュメントにも記載がある通り料金ページの方が正となっているためです。

AWS Price List API は料金詳細を参考としてのみ提供します。オファーファイルとサービスの料金ページの間に不一致がある場合、AWS はサービスの料金ページに表示されている料金を請求します。

AWS Price List API の使用 - AWS 請求情報とコスト管理

最後に

CLI上でコマンドをパイプでつなげてワンライナーでやるのはスマートな感じがしますが、ちょっと加工したい場合はPythonのようなスクリプトでやる方が簡単ですよね。参考になれば幸いです。