Edited at

IOS XEのModel Driven Telemetryを試してみた

More than 1 year has passed since last update.


はじめに

この記事はシスコの有志による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のベータイメージで試しています :bow:

近いうちに、ルータ系のプラットフォームでも正式にサポートされるはずです :yum:


デバイスの設定

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

(具体的にどういった情報を取得できるか等、)詳細は以下のドキュメントをご参照ください。


素の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上では解放されているようでしたが、プロダクション環境ではくれぐれもマネしないでください :sweat_smile:

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

subsystemnetconfにする以外は、通常の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プラグインを作ってみましたー :sushi:

具体的な使い方は、近日、別な記事で紹介するようにします!

おしまい