概要
NetOpsCoding Advent Calendar 2015の16日目の記事です。
今回は、Junos 14.2から搭載されたREST APIを使って、Junos機器の状態を取得したり、設定を変更したりしてみたいと思います。
Junos REST APIについて
Junos REST APIは、JuniperのM/T/(v)MX/PTXルータでJunos 14.2からサポートされた、HTTP(S)経由で使用できるAPIです。
APIについてのドキュメントは以下にまとまっています。
REST API Guide
また、APIで使用するRPCについては基本的にNETCONFと同様であり、以下のドキュメントに記載されています。
NETCONF XML Management Protocol Developer Guide
環境
今回は、以下の環境を使用して検証を行いました。
- Ruby 1.9.3-p484
- ライブラリ
- rest-client 1.8.0
- ルータ
- Juniper vMX 15.1F3.11
事前準備
ルータ側
リモートの環境からアクセスできるよう、IPアドレスやユーザ等の設定を行っておきます。
REST APIは以下の設定を投入することによって有効化されます。
set system services rest http port 3000
PC側
Rubyでrest-clientライブラリが使用できるようにします。(gem install --no-ri --no-rdoc rest-client
等、お好みに応じてインストールしてください。)
情報取得(GET)
まずはじめに、機器の情報を取得してみましょう。Junosでは、各コマンドに対してそれぞれRPC methodが定義されています。各コマンドに対応するRPCの情報は以下のようにして確認することが可能です。
root@vmx> show version | display xml rpc
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/15.1F3/junos">
<rpc>
<get-software-information>
</get-software-information>
</rpc>
<cli>
<banner></banner>
</cli>
</rpc-reply>
この例では、RPCとしてget-software-information
methodを発行することにより、Junos CLIでいうshow version
と同一の出力が得られることがわかります。
早速、APIを叩いてみましょう。
以下にサンプルコードを提示します。HTTP GETにて、get-software-information
RPC methodを叩き、結果を出力します。
#!/usr/bin/env ruby
require 'restclient'
auth = "user:password"
host = '192.168.0.1'
port = '3000'
rpc = "http://#{auth}@#{host}:#{port}/rpc"
puts RestClient.get rpc + '/get-software-information'
出力結果:
<software-information>
<host-name>vmx</host-name>
<product-model>vmx</product-model>
<product-name>vmx</product-name>
<junos-version>15.1F3.11</junos-version>
<package-information>
<name>junos</name>
<comment>JUNOS Base OS boot [15.1F3.11]</comment>
</package-information>
<package-information>
<name>jbase</name>
<comment>JUNOS Base OS Software Suite [15.1F3.11]</comment>
</package-information>
<!-- ...(中略)... -->
<package-information>
<name>jsim-vmx</name>
<comment>JUNOS Packet Forwarding Engine Trio Simulation Package [15.1F3.11]</comment>
</package-information>
</software-information>
出力結果を見るとわかるように、デフォルトでは応答形式はXMLになっていますので、適切なライブラリを使用する事により、プログラム内で特定の箇所を抽出する事も可能になってきます。
ここで、「えっ、今時XMLかよ…」などと思われた読者の方も多いんじゃないかと思いますが、JSONやPlain textでの出力も可能です。例えば、JSONで結果を取得したい場合、HTTPのAcceptヘッダに'application/json'を指定するか、URLの末尾に@format=json
と追記することでも出力フォーマットを指定可能です。
例として、Junos CLIでいうshow interfaces terse ge-0/0/1
の結果をJSONで出力してみます。
#!/usr/bin/env ruby
require 'restclient'
auth = "user:password"
host = '192.168.0.1'
port = '3000'
rpc = "http://#{auth}@#{host}:#{port}/rpc"
puts RestClient.get rpc + '/get-interface-information?terse=&interface-name=ge-0/0/1', :accept => 'application/json'
# alternative:
# puts RestClient.get rpc + '/get-interface-information@format=json?terse=&interface-name=ge-0/0/1'
出力結果:
{
"interface-information" : [
{
"attributes" : {"xmlns" : "http://xml.juniper.net/junos/15.1F3/junos-interface",
"junos:style" : "terse"
},
"physical-interface" : [
{
"name" : [
{
"data" : "ge-0/0/1"
}
],
"admin-status" : [
{
"data" : "up"
}
],
"oper-status" : [
{
"data" : "down"
}
],
"logical-interface" : [
{
"name" : [
{
"data" : "ge-0/0/1.0"
}
],
"admin-status" : [
{
"data" : "up"
}
],
"oper-status" : [
{
"data" : "down"
}
],
"filter-information" : [
{
}
],
"address-family" : [
{
"address-family-name" : [
{
"data" : "inet"
}
],
"interface-address" : [
{
"ifa-local" : [
{
"data" : "10.0.0.2/24",
"attributes" : {"junos:emit" : "emit"}
}
]
}
]
},
{
"address-family-name" : [
{
"data" : "multiservice"
}
]
}
]
}
]
}
]
}
]
}
REST APIを使用するとこのように、簡単にJunos機器の状態を取得することが可能です。
設定の変更
さて、続いては設定の変更を行ってみたいと思います。
設定の変更は、HTTP POSTでload-configuration
RPCを叩くことにより可能です。
例えば、ge-0/0/1
のDescriptionにto-customer-A
という文字列を指定するコードは以下のようになります。Candidate Configurationの変更を行うのみですので、Commitは行われません。
#!/usr/bin/env ruby
require 'restclient'
auth = "user:password"
host = '192.168.0.1'
port = '3000'
rpc = "http://#{auth}@#{host}:#{port}/rpc"
puts RestClient.post rpc + '/load-configuration@format=text',
'<configuration-text>
interfaces {
ge-0/0/1 {
description to-customer-A
}
}
</configuration-text>'
結果:
<load-configuration-results>
<load-success/>
</load-configuration-results>
ここで、Junos上でshow | compare
を実行してみると、上記の設定が正しくCandidate Configurationに投入されていることがわかります。
root@vmx# show | compare
[edit interfaces ge-0/0/1]
+ description to-customer-A;
この変更を適用するには、投入した設定をCommitする必要があります。先ほどのコードに、Commitする操作を追加してみましょう。
#!/usr/bin/env ruby
require 'restclient'
auth = "user:password"
host = '192.168.0.1'
port = '3000'
rpc = "http://#{auth}@#{host}:#{port}/rpc"
puts RestClient.post rpc + '/load-configuration@format=text',
'<configuration-text>
interfaces {
ge-0/0/1 {
description to-customer-A
}
}
</configuration-text>'
puts RestClient.get rpc + '/commit-configuration'
結果:
<load-configuration-results>
<load-success/>
</load-configuration-results>
<commit-results xmlns:junos="http://xml.juniper.net/junos/*/junos">
<routing-engine junos:style="normal">
<name>re0</name>
<commit-success/>
</routing-engine>
</commit-results>
2つのRPCリクエストが行われ、設定の変更が反映されたようです。Junos上で確認してみましょう。
[edit]
root@vmx# show | compare rollback 1
[edit interfaces ge-0/0/1]
+ description to-customer-A;
Multiple RPC call per one HTTP request
ここまでは、1つのHTTPリクエストで1つのRPCコールを行ってきました。
しかし、実際にコンフィグレーションを変更したりする場合、1つのリクエストの中で複数の変更を行ったり、Commit(設定の反映)を行ないたいケースが多々出てくるかと思います。
Junos REST APIでは、rpc endpointに複数のRPC methodを含むXMLをPostすることにより、1回のHTTPリクエストの中で複数のRPCコールを発行することが可能です。
例えば、Configurationをロックした後、ge-0/0/0のdescriptionを追加し、ge-0/0/1のdescriptionを削除してCommitしてからConfigurationのロックを解除するコードは以下のようになります。
#!/usr/bin/env ruby
require 'restclient'
auth = "user:password"
host = '192.168.0.1'
port = '3000'
rpc = "http://#{auth}@#{host}:#{port}/rpc"
puts RestClient.post rpc+'?stop-on-error=1' ,
'<lock-configuration />
<load-configuration format="text">
<configuration-text>
interfaces {
ge-0/0/0 {
description to-core-router;
}
ge-0/0/1 {
delete: description;
}
}
</configuration-text>
</load-configuration>
<commit-configuration />
<unlock-configuration />'
結果:
--nwlrbbmqbhcdarz
--nwlrbbmqbhcdarz
Content-Type: text/plain; charset=utf-8
<load-configuration-results>
<load-success/>
</load-configuration-results>
--nwlrbbmqbhcdarz
Content-Type: application/xml; charset=utf-8
<commit-results xmlns:junos="http://xml.juniper.net/junos/*/junos">
<routing-engine junos:style="normal">
<name>re0</name>
<commit-success/>
</routing-engine>
</commit-results>
--nwlrbbmqbhcdarz
--nwlrbbmqbhcdarz--
複数のRPC methodを1度のリクエストで実行した場合、multipart/mixedの形でそれぞれのRPCに対応する応答が返されるようです。
さいごに
今回はJunosのREST APIを使用して、ルータをプログラムから操作してみました。REST APIによって実現できること自体は特にNetconfと変わりませんが、汎用のHTTPライブラリのみで操作できるため、比較的とっつきやすいのではないかと思います。
現在はルータ系のJunos 14.2以降でのみサポートされていますが、スイッチではJunos 15.1以降でサポートされる予定です。今後の流行がどういった方向に進むのかはわかりませんが、Junos機器を自動化するにあたっての選択肢の一つとして考えられるかと思います。