はじめに
この記事はシスコの有志によるCisco Systems Japan Advent Calendar 2017の21日目です。
IOS XE 16.6.1からModel Driven Telemetryをサポートしましたので、今回はこれを試してみたいと思います。
ちょうど昨日のCisco Advent Calender 20日目で@muramurakamiさんがIOS XRのTelemetryに関連した投稿をしてくれているので、そちらもぜひ参考にしてみてください。
Model Driven Telemetry
Model Driven Telemetryは、(IETF NETCONF YANG Push1やOpenConfig Streaming Telemetry2のように)Push型かつストリーミング方式でリアルタイムにデバイスからコンフィグおよびステートの情報を取得するためのテクノロジー一般を指すCisco用語です。モデル駆動(Model Driven)と冠するだけあって、ネットワークデバイスから提供される情報がYANGでモデル化されています。IOS XEとIOS XRでは、(コーデックやトランスポート等の)実装方式が異なるのですが、いずれはOpenConfig Streaming Telemetryなど、現在サポートされていない仕組みも取り込んでいくことも見据えて、このような包括的なネーミングになってます。
IOS XRにおけるModel Driven Telemetryの実装は、OpenConfig Streaming Telemetryの影響を反映しているのに対して、IOS XEにおけるModel Driven Telemetryのそれは、標準化作業中のIETF NETCONF YANG Pushドラフトを基調としてます。
前提
この記事では、IOS XEプラットフォームとして、すでにModel Driven Telemetryを正式にサポートしているCatalyst系のプラットフォームではなく、CSR1000Vのベータイメージで試しています
近いうちに、ルータ系のプラットフォームでも正式にサポートされるはずです
デバイスの設定
YANGに対応したNETCONFを有効にします。
Router(config)#netconf-yang
さらに、NETCONFでアクセスするためのアカウントを作成します。privilege
レベルは15
にする必要があります。
Router(config)#username admin privilege 15 password admin
今回はCPU使用率しか取得しないので、以下の設定は不要ですが、取得したいデータに応じて、デバイス内部における(IOS上で動作する)NETCONFエージェント(実態はConfD3)とIOS内部のデータストアを同期する方式と間隔を設定します。一見分かりづらいのですが、以下のような関係にあります。NETCONFクライアントはNETCONFエージェントと会話しますが、ここで設定しているのは、IOSデバイス内部におけるNETCONFエージェントとIOSデータストアの関係です。
[NETCONFクライアント]----------[NETCONFエージェント(ConfD)]----------[IOSデータストア]
Router(config)#netconf-yang cisco-odm polling-enable
Router(config)#netconf-yang cisco-odm actions OSPF
Router(config-odm-action)#mode poll
Router(config-odm-action)#polling-interval 1000
(具体的にどういった情報を取得できるか等、)詳細は以下のドキュメントをご参照ください。
- Programmability Configuration Guide, Cisco IOS XE Everest 16.6.x: Configuring YANG Datamodel
- Programmability Configuration Guide, Cisco IOS XE Everest 16.6.x: Operational Data Parser Polling
素のSSHで試す
まずは、仕組みを知る上で、素のSSHで接続して試します(以下、デバイスとクライアントの間でやりとりしているXMLメッセージは、可読性をよくするため整形してます)。
クライアントからデバイスにSSHします。重要な点として、-s
オプションでsubsystem
としてnetconf
を指定します。
$ ssh admin@192.0.2.1 -p 830 -s netconf
admin@192.0.2.1's password:*****
接続すると、デバイスから<hello>
が返ってきます。<capability>
の量が膨大なので省略してます。
client <- device
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
...
</capabilities>
<session-id>237955</session-id>
</hello>]]>]]>
クライアントからも<hello>
を送信します。
client -> device
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
</capabilities>
</hello>]]>]]>
続けて、クライアントから<establish-subscription>
オペレーションを送信します。このときに、xpath-filter
で、取得する情報の対象を特定するのと、period
で情報をPushしてもらう間隔をセンチ秒(1/100秒)単位で指定します。以下の例では、5秒間のCPU使用率に関する情報(/ios-emul-oper-db:ios-emul-oper-db/cpu-usage/five-seconds
)が10秒間隔でデバイスからPushされることになります。
client -> device
<?xml version="1.0" encoding="utf-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<establish-subscription xmlns="urn:ietf:params:xml:ns:yang:ietf-event-notifications" xmlns:yp="urn:ietf:params:xml:ns:yang:ietf-yang-push">
<stream>yp:yang-push</stream>
<yp:xpath-filter>/ios-emul-oper-db:ios-emul-oper-db/cpu-usage/five-seconds</yp:xpath-filter>
<yp:period>1000</yp:period>
</establish-subscription>
</rpc>]]>]]>
<establish-subscription>
オペレーションに問題がなければ、以下のような<rpc-reply>
がデバイスから返ってきます。
このとき通知される<subscription-id>
は、のちにSubscriptionを停止する際に必要になります。
client <- device
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><subscription-result xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications' xmlns:notif-bis="urn:ietf:params:xml:ns:yang:ietf-event-notifications">notif-bis:ok</subscription-result>
<subscription-id xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications'>2147483648</subscription-id>
</rpc-reply>]]>]]>
以降、period
で指定した間隔で、以下のような情報が継続的にPushされてきます。
client <- device
<?xml version="1.0" encoding="UTF-8"?>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2017-12-15T05:01:44.32Z</eventTime>
<push-update xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-push">
<subscription-id>2147483648</subscription-id>
<datastore-contents-xml>
<ios-emul-oper-db xmlns="http://cisco.com/ns/iosxe/ios-emul-oper-db">
<cpu-usage>
<five-seconds>0</five-seconds>
</cpu-usage>
</ios-emul-oper-db>
</datastore-contents-xml>
</push-update>
</notification>
一応、デバイス側でSubscriptionの状態を確認しておきましょう。
Router#show telemetry ietf subscription all
Telemetry subscription brief
ID Type State Filter type
-----------------------------------------------------
2147483648 Dynamic Valid xpath
<subscription-id>
を指定することでセッション毎の詳細を確認することができます。
Router#show telemetry ietf subscription 2147483714 detail
Telemetry subscription detail:
Subscription ID: 2147483648
Type: Dynamic
State: Valid
Stream: yang-push
Filter:
Filter type: xpath
XPath: /ios-emul-oper-db:ios-emul-oper-db/cpu-usage/five-seconds
Update policy:
Update Trigger: periodic
Period: 1000
Encoding: encode-xml
Source VRF:
Source Address:
Notes:
<delete-subscription>
オペレーションでSubscriptionを停止します。<subscription-id>
には、 <establish-subscription>
オペレーションの際に通知されらものを使用します。
client -> device
<?xml version="1.0" encoding="utf-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<delete-subscription xmlns="urn:ietf:params:xml:ns:yang:ietf-event-notifications" xmlns:netconf="urn:ietf:params:xml:ns:netconf:base:1.0">
<subscription-id>2147483648</subscription-id>
</delete-subscription>
</rpc>]]>]]>
<delete-subscription>
オペレーションに問題がなければ、以下のような<rpc-reply>
がデバイスから返ってきます。
client <- device
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
<subscription-result xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications' xmlns:notif-bis="urn:ietf:params:xml:ns:yang:ietf-event-notifications">notif-bis:ok</subscription-result>
</rpc-reply>]]>]]>
最後に、<close-session>
オペレーションでNETCONFセッション自体を終了します。
client -> device
<?xml version="1.0" encoding="utf-8" ?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<close-session/>
</rpc>]]>]]>
デバイスからは以下のような<rpc-reply>
が返って、SSHセッションが終了します。
client <- device
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><ok/>
</rpc-reply>]]>]]>
Rubyで試す
次に、プログラミングを用いた例として、Rubyで試してみます。なぜRubyなのかといえば、最後に紹介するようにFluentdのプラグインを作りたかったからです。
サンプルコードは以下のとおりです。後述する理由により、subscription-id
を取得するためにXMLメッセージをパースすると、割と込み入ったサンプルコードになってしまうため、<delete-subscription>
オペレーションと<close-session>
オペレーションをしないままで、SSHセッションを終了してます。showコマンド見る限り、SSHセッション自体を終了すれば、それに紐づくNETCONF YANG PushのSubscriptionはIOS上では解放されているようでしたが、プロダクション環境ではくれぐれもマネしないでください
require 'net/ssh'
int_pressed = false
trap :INT do
int_pressed = true
end
hello = <<-EOS
<?xml version="1.0" encoding="utf-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
</capabilities>
</hello>]]>]]>
EOS
establish_subscription = <<-EOS
<?xml version="1.0" encoding="utf-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<establish-subscription xmlns="urn:ietf:params:xml:ns:yang:ietf-event-notifications" xmlns:yp="urn:ietf:params:xml:ns:yang:ietf-yang-push">
<stream>yp:yang-push</stream>
<yp:xpath-filter>/ios-emul-oper-db:ios-emul-oper-db/cpu-usage/five-seconds</yp:xpath-filter>
<yp:period>1000</yp:period>
</establish-subscription>
</rpc>]]>]]>
EOS
Net::SSH.start("192.0.2.1", "admin", :port => 830, :password => "admin", :timeout => 10) do |ssh|
ssh.open_channel do |channel|
channel.subsystem("netconf") do |ch, success|
raise "subsystem could not be started" unless success
ch.on_data do |ch, data|
puts "on data..."
puts data
end
ch.on_close do |ch|
puts "on close ..."
end
ch.on_eof do |ch|
puts "on eof ..."
end
puts "send hello ..."
ch.send_data(hello)
puts "send subscription ..."
ch.send_data(establish_subscription)
end
end
ssh.loop(1) { not int_pressed }
puts "close ssh ..."
ssh.close
end
subsystem
をnetconf
にする以外は、通常のnet-sshの使い方のとおりです。
実行すると、以下のような結果になります。<hello>
の中身は今回も省略してます。
send hello ...
send subscription ...
on data...
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
...
</capabilities>
<session-id>30628</session-id></hello>]]>]]><?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><subscription-result xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications' xmlns:notif-bis="urn:ietf:params:xml:ns:yang:ietf-event-notifications">notif-bis:ok</subscription-result>
<subscription-id xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications'>2147483653</subscription-id>
</rpc-reply>]]>]]>
on data...
<?xml version="1.0" encoding="UTF-8"?>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2017-12-19T04:00:51.70Z</eventTime><push-update xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-push"><subscription-id>2147483653</subscription-id><datastore-contents-xml><ios-emul-oper-db xmlns="http://cisco.com/ns/iosxe/ios-emul-oper-db"><cpu-usage><five-seconds>0</five-seconds></cpu-usage></ios-emul-oper-db></datastore-contents-xml></push-update></notification>
]]>]]>
on data...
<?xml version="1.0" encoding="UTF-8"?>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2017-12-19T04:01:01.70Z</eventTime><push-update xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-push"><subscription-id>2147483653</subscription-id><datastore-contents-xml><ios-emul-oper-db xmlns="http://cisco.com/ns/iosxe/ios-emul-oper-db"><cpu-usage><five-seconds>0</five-seconds></cpu-usage></ios-emul-oper-db></datastore-contents-xml></push-update></notification>
on data...
]]>]]>
on data...
<?xml version="1.0" encoding="UTF-8"?>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2017-12-19T04:01:11.70Z</eventTime><push-update xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-push"><subscription-id>2147483653</subscription-id><datastore-contents-xml><ios-emul-oper-db xmlns="http://cisco.com/ns/iosxe/ios-emul-oper-db"><cpu-usage><five-seconds>1</five-seconds></cpu-usage></ios-emul-oper-db></datastore-contents-xml></push-update></notification>
]]>]]>
...
実行結果を注意深くみると分かりますが、一つのRPCメッセージが大きいと、デバイスからは複数回に分断されてメッセージが届きます(つまり、複数回on_dataが呼び出されます)。サンプルコードの複雑化を避けるため今回は割愛しましたが、XML形式の文字列をパースしたいときは、何らかの変数にいったん格納してから、デリミタ(]]>]]>
)が現れたタイミングでまるっとパースする等の工夫が必要です。
おまけ
記事の中でも前振りしたとおり、IOS XEのModel Driven Telemetry用にFluentdのInputプラグインを作ってみましたー
具体的な使い方は、近日、別な記事で紹介するようにします!
おしまい