PeeringDB2.0 APIを使ってみる

# 概要
[NetOpsCoding AdventCalender](http://qiita.com/advent-calendar/2015/netopscoding)の９日目の記事です。

今回は、[PeeringDB2.0](http://docs.peeringdb.com/)で公開されているAPIを使って、ASの情報を取得するプログラムを作ってみます。

# Peeringとは
[AS番号](https://www.nic.ad.jp/ja/ip/asnumber.html)を持つ組織のネットワーク同士が相互接続することを、PeerやPeeringと呼ばれています。具体的には[BGP](https://www.nic.ad.jp/ja/newsletter/No35/0800.html)というプロトコルを両組織のルータで動作させ、お互いの組織のIPアドレスやAS番号を設定することでPeeringを実現します。

Peeringにはいくつか方法がありますが、ここでは詳しくは触れません。詳細が気になる方は、
たくさん情報が公開されてますので、以下の資料などを参照してください。
http://www.slideshare.net/taijitsuchiya5/wakamonog6-tsuchiya-public

# PeeringDBとは
仮に「組織Aのネットワークと繋がりたい。」と思ったとします。その際に「組織AのAS番号は何か」「どのDC拠点に接続用ルータを置いているか」「どのIX事業者を使っているか」「どのIPアドレスを使っているか」「コンタクト先はどこか」などの情報を知る必要があります。

Peeringを検討し、相手組織の情報を調べたい場合に、世界的に最も多く利用される情報が[PeeringDB](www.peeringdb.com)です。PeeringDBは非営利団体で開発・運用されているWebシステムであり、ASを保有する世界中の組織がPeeringDBに情報を掲載しています。
(ただし、情報の掲載はASを保有する組織に委ねらているので、情報が古い場合やそもそも情報が掲載されていない場合もあります。)

2015年にPeeringDB2.0がベータバージョンとして公開されました。以前のPeeringDBとはUIや機能が大幅に改良されており、RESTful APIによる情報取得ができるようになっています。
http://docs.peeringdb.com/


# PeeringDBを使ってみる
PeeringDBは誰でも無料で見ることができます。
例えばBIGLOBEのAS情報は以下の様に調べることができます。
http://as2518.peeringdb.com/　

PeeringDBを調べることで「会社情報」「AS番号」「大まかなトラフィック流量」「Peeringポリシー（Peeringの判断基準）」「Peering拠点」などの情報を知ることができます。

PeeringDB2.0でもほぼ同じ内容が公開されています。
https://beta.peeringdb.com/net/2229

# PeeringDB 2.0 APIを使ってみる
PeeringDB2.0 APIの仕様は以下のページで公開されています。
https://beta.peeringdb.com/apidocs/

例えばBIGLOBE(AS2518)の情報を一括して取得したい場合は、以下のようにアクセスします。


```
# Linux/Macのターミナルで取得する場合
% curl https://beta.peeringdb.com/api/asn/2518

{
  "meta": {}, 
  "data": [
    {
      "id": 2229, 
      "org_id": 2934, 
      "org": {
        "id": 2934, 
        "name": "BIGLOBE Inc.", 
        "website": "", 
        "notes": "", 
        "net_set": [
          2229
        ], 
     
      (一部省略)

      "name": "BIGLOBE Inc.", 
      "aka": "FullRoute", 
      "website": "http://www.biglobe.co.jp/en/", 
      "asn": 2518, 
      "looking_glass": "http://lg.fullroute.net/lg/", 
      "route_server": "", 
      "irr_as_set": "AS-MESH", 
      "info_type": "NSP", 
      "info_prefixes4": 750, 
      "info_prefixes6": 750, 
      "info_traffic": "200-300 Gbps", 
      "info_ratio": "Balanced", 
      "info_scope": "Asia Pacific", 
      "info_unicast": true, 
      "info_multicast": false, 
      "info_ipv6": true, 
      "notes": "We are connected to BBIX Singapore but not Hong Kong. We will only peer with Singapore switch participants.\r\n\r\nYou can ping and trace to ping.mesh.ad.jp(both IPv4 and IPv6).\r\n\r\nIRR Records should be viewed with source set to RADB or JPIRR. There are colliding records with the same AS-SET name, but from a different AS on RIPE. \r\n\r\nSee here for Japanese version of the peering policy http://www.biglobe.co.jp/pdf/PeeringPolicy2013_jp_03.pdf",
      "policy_url": "http://www.biglobe.co.jp/en/peering_policy.pdf",
      "policy_general": "Selective",
      "policy_locations": "Preferred",
      "policy_ratio": false,
      "policy_contracts": "Not Required",
      "netfac_set": [
        {
          "id": 17472,
          "fac_id": 19,
          "avail_sonet": false,
          "avail_ethernet": true,
          "avail_atm": false,
          "local_asn": 2518,
          "created": null,
          "updated": "2015-12-05T04:29:57Z",
          "status": ""
        },
        {
          "id": 9426,
          "fac_id": 142,
          "avail_sonet": false,
          "avail_ethernet": true,
          "avail_atm": false,
          "local_asn": 2518,
          "created": null,
          "updated": "2015-12-05T04:25:36Z",
          "status": ""
        },
        (以下、省略)
```

ここで注意したいのが"netfac_set"の項目にDC情報やIX情報が含まれているのですが、"netfac_set"のデータは別のテーブルを外部キーとして参照しているため、IDしか含まれていません。IX情報などを更に知りたい場合は、別のURLを作って実行する必要があります。

例えば "netfac_set"の中の"fac_id": 19で指定されている情報をさらに見たい場合には、以下のようなURLでHTTPリクエストを実行する必要があります。

```
% curl https://beta.peeringdb.com/api/fac/19                                                                                                                                                 (git)-[master]
{
  "meta": {},
  "data": [
    {
      "id": 19,
      "org_id": 618,
      "org_name": "CoreSite",
      "org": {
        "id": 618,
        "name": "CoreSite",
        "website": "",
        "notes": "",
        "net_set": [],
        "fac_set": [
          19,
          20,
          286,
          363,
          440,
          584,
          668,
          669,
          670,
          671,
          1229,
          1619,
          1671
        ],
        "ix_set": [],
        "address1": "",
        "address2": "",
        "city": "",
        "country": "",
        "state": "",
        "zipcode": "",
        "created": "2015-10-12T08:14:45Z",
        "updated": "2015-12-05T09:07:24Z",
        "status": "0"
      },
      "name": "CoreSite - LA1 - One Wilshire",
      "website": "http://www.coresite.com",
      "clli": "LSANCA",
      "rencode": "",
      "npanxx": "213-489",
      "notes": "",
      "created": null,
      "updated": "2015-12-05T09:05:59Z",
      "status": "0",
      "address1": "624 S. Grand Ave.",
      "address2": "Suite 110",
      "city": "Los Angeles",
      "country": "US",
      "state": "CA",
      "zipcode": "90017"
    }
  ]
}%
```

PeeringDB APIでは、https://beta.peeringdb.com/apidocs/ で公開されているように、AS情報(asn)、DC情報(fac)、IX情報(ix)など情報テーブルが分かれて保持されており、外部キーとして参照されています。

このように、知りたい情報をAPIで取得する場合には、外部キーを取得し、URLを変更しながら複数回HTTPリクエストを実行する必要があります。


# PeeringDB2.0 APIで情報取得するプログラム

APIを使って情報取得するプログラムを作ります。

ここでは、以下のように引数にAS番号を与えて実行することで、その会社情報と接続している全IXの情報を出力するPythonプログラムを作りました。

```
% python get_peeringdb.py <対象のAS番号>
```

プログラムはgithubでも公開しています。
https://github.com/netops-coding/sample_peeringdb

```py:get_peeringdb.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

from urllib2 import urlopen, HTTPError
from json import loads
import sys

def get_peeringdb(pdp_type, pdp_id):
    pdb_url = 'https://beta.peeringdb.com/api/%s/%s'% (pdp_type, pdp_id)

    try :
        result_json = urlopen(pdb_url).read()
        result_dict = loads(result_json)
    except HTTPError, err:
        if err.code == 404:
            return None
    return result_dict


if __name__ == '__main__' :
    as_num = sys.argv[1]

    # get company and AS information
    as_info_dict = get_peeringdb('asn', as_num)
    company_name = as_info_dict["data"][0]["org"]["name"]
    company_website = as_info_dict["data"][0]["website"]

    print as_num
    print company_name
    print company_website

    print '='*30

    # get IX information
    for netixlan_set in as_info_dict["data"][0]["netixlan_set"]:
        ixlan_id = netixlan_set["ixlan_id"]
        ipaddr4 = netixlan_set["ipaddr4"]
        ipaddr6 = netixlan_set["ipaddr6"]

        ixlan_info_dict = get_peeringdb('ixlan', ixlan_id)
        ix_name =  ixlan_info_dict["data"][0]["ix"]["org"]["name"]

        print ix_name
        print ipaddr4
        print ipaddr6
        print '='*30
```

以下のコマンドで実行します。ここではBIGLOBE(AS2518)を指定しています。

```
% python get_peeringdb.py 2518

                                                                                                                                                         (git)-[master]
2518
BIGLOBE Inc.
http://www.biglobe.co.jp/en/
==============================
Equinix Osaka
None
None
==============================
Equinix San Jose
206.223.116.156
2001:504:0:1::2518:1
==============================
Equinix Singapore
202.79.197.180
2001:de8:4::2518:1
==============================
HKIX
123.255.90.157
2001:7fa:0:1::ca28:a09d
==============================
Equinix Tokyo
203.190.230.56
2001:de8:5::2518:2
==============================
Equinix Tokyo
203.190.230.83
2001:de8:5::2518:3
==============================
SGIX
103.16.102.69
2001:de8:12:100::69
==============================
CoreSite - Any2 California
206.72.210.154
2001:504:13::210:154
==============================
BBIX Hong Kong / Singapore
103.231.152.25
2001:df5:b800:bb00::2518:1
==============================
JPIX
210.171.224.181
2001:de8:8::2518:2
==============================
BBIX Tokyo
218.100.6.47
2001:de8:c::2518:1
==============================
BBIX Tokyo
218.100.6.73
2001:de8:c::2518:2
==============================
JPNAP Tokyo
210.173.176.14
2001:7fa:7:1::2518:2
==============================
JPNAP Tokyo
210.173.176.124
2001:7fa:7:1::2518:3
==============================
```

以上のように、AS番号を指定するだけで、その組織が接続されているIX情報の一覧情報を取得するプログラムを作ることができました。

# 公開されているPythonパッケージ
今回作成したプログラムを見ていると「 as_info_dict["data"][0]["org"]["name"] 」といった、PeeringDB2.0のデータ構造をそのまま利用しており、第三者がプログラムを見た時に非常に分かりづらく、追加機能を開発する際にもデータ構造を調べながら開発を進める必要があり煩雑になりがちです。

すで公開されているPythonパッケージを利用することで、こういったデータ構造を意識せずに、ソフトウェアを開発することができます。

私が見つけた中では、以下の２つのPythonパッケージがありました。結論を言うと、ソフトウェア開発を楽に進めるためには、２つめのパッケージが有力なのですがトラブルがあり、まだ完全には使いにくい状況です。２つ目の改修や追加機能の開発を期待しています。

- PeeringDB Python Client
    - http://peeringdb.github.io/peeringdb-py
    - PeeringDB開発者Matt Griswoldが開発している公式Pythonパッケージです。
    - 試しに使ってみましたが、機能が少なく(all,get,create,update,saveなど基本的なものだけ)、今回開発したプログラムと同じく「リクエストを作成・実行 -> 外部キーを取得 -> 別リクエストを作成・実行」という流れが必要になるため、あまり楽にはならない印象です。
      
- Netflix/peeringdb-py
    - https://github.com/Netflix/peeringdb-py
    - NetFlixがOSSとして公開しているPeeringDBクライアントです。
    - 前者に比べて多機能で、非常に便利な関数が実装されています。例えば「 print pdb.matching_ixlan([2906, 5089])」と書くことで、２つのASの共通するIXを表示してくれるといった、ネットワーク運用者にとってありがたい機能が実装されています。
    - 非常に残念なことに、試しに使おうとインストールしたところ「$ sudo pip install peeringdb」を実行すると、なんと前者のパッケージと名前が重複しているため、現時点でpipコマンドではインストールできません。   [Issue](https://github.com/Netflix/peeringdb-py/issues/17)としても挙げられているためそのうち改修されるかもしれませんが、今すぐどうしても使いたい場合は、git clobeで直接ファイルを利用することをおすすめします。


# 最後に
今回は、PeeringDBで公開されている情報をAPIを介して取得するソフトウェアを作りました。自社ネットワークとPeeringしている会社情報やネットワーク情報をデータベース化する際にはPeeringDBの公開情報から取得すると非常に便利です。機会があればぜひ使ってみてください。