😃

GoでgRPCを使ってIOS XRからTelemtryを収集する

9 min read

Telemetryの取得方法は様々あり、最近ではOSSのcollectorが作られていることもあって、こと統計情報取得に関してはSNMPから置き換わる日も遠くなさそうである。

とはいえ、ElasticsearchやinfluxDB等、成熟した製品があるデータストアと比べ、TelemetryのCollectorは発展途上なのも事実。

Pythonを使った手法は以前、ネットワークプログラマビリティ勉強会で紹介したので、今回はGoを用いて、CiscoのIOS XRからDial outでModel Driven Telemetryを取得する方法を紹介する。(pipelineがまさしくGoで書かれたCollectorそのものだが、枝葉が多くて読み解きにくいので)

なお、エラーハンドリングなどは完全に省略しているので、必要に応じて追加して頂きたい。

手軽にIOS XRを試すには、AWSでXRv 9000を立てればいい。

Telemetry用には以下のようなconfigを投入する。

telemetry model-driven
 destination-group gollector
  address-family ipv4 192.168.1.100 port 2103
   encoding self-describing-gpb
   protocol grpc no-tls
  !
 !
 sensor-group Perf
  sensor-path Cisco-IOS-XR-wdsysmon-fd-oper:system-monitoring/cpu-utilization
 !
 subscription PerfStats
  sensor-group-id Perf sample-interval 5000
  destination-id gollector
  source-interface MgmtEth0/RP0/CPU0/0
 !
!

ここでは、XRvからサーバ(192.168.1.100)のport 2103宛に、CPU使用率の情報を投げる設定を入れている。お試しなので、TLSは使わず、トランスポートにgRPC、データフォーマットにGPBを使う。

サーバ側はなんでもいいが、今回CentOS8.2を使った。SELinuxとfirewalldの設定を変えておく。

setenforce 0
systemctl stop firewalld

また、Goをインストールしてパスを通しておく。

wget http://golang.org/dl/go1.15.2.linux-amd64.tar.gz
tar -C /usr/local -zxf go1.15.2.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

目的に則したGo用のファイル郡はすでに用意されており、今回はDial out用のファイルを参照する。このファイルでserver apiは以下のように定義されていることから、

type GRPCMdtDialoutServer interface {
	MdtDialout(GRPCMdtDialout_MdtDialoutServer) error
}

MdtDialoutメソッドを実装すればよいことがわかる。以下のような感じ。

package main

import (
	dialout "github.com/cisco/bigmuddy-network-telemetry-proto/proto_go/mdt_grpc_dialout"
)

type server struct{}

func (s *server) MdtDialout(stream dialout.GRPCMdtDialout_MdtDialoutServer) error {
    //ここに受け取ったデータに対する処理を書く
}

次に、gRPCのserverを立てる。

package main

import (
    "net"
    "google.golang.org/grpc"
	dialout "github.com/cisco/bigmuddy-network-telemetry-proto/proto_go/mdt_grpc_dialout"
)

type server struct{}

func (s *server) MdtDialout(stream dialout.GRPCMdtDialout_MdtDialoutServer) error {
	//ここに受け取ったデータに対する処理を書く
}

func main() {

	listen, _ := net.Listen("tcp", "0.0.0.0:2103")

	s := grpc.NewServer()
	dialout.RegisterGRPCMdtDialoutServer(s, &server{})

	s.Serve(listen)

}

Model Driven Telemetryは、このファイルに記載された形式で送られてくる。(Dial outで受け取った戻り値のMdtDialoutArgsに含まれるData []byteがこの形式)

そこで、その定義に従いUnmarchalしてやる。また、self-describing gpbの場合、DataGpbkvにTelemetryデータが格納される。それらを勘案して、以下のようにすればデータを取得できる(大量のデータが送られてくるので注意)。

package main

import (
    "fmt"
    "net"
    "google.golang.org/grpc"
    "github.com/golang/protobuf/proto"
    telem "github.com/cisco/bigmuddy-network-telemetry-proto/proto_go"
	dialout "github.com/cisco/bigmuddy-network-telemetry-proto/proto_go/mdt_grpc_dialout"
)

type server struct{}

func (s *server) MdtDialout(stream dialout.GRPCMdtDialout_MdtDialoutServer) error {
    for {
        msg, _ := stream.Recv()
        
        telem := &telem.Telemetry()
        err := proto.Unmarshal(msg.Data, telem)
        
        if telem.DataGpbkv != nil {
            fmt.Println(telem.DataGpbkv)
        }
    }
}

func main() {

	listen, _ := net.Listen("tcp", "0.0.0.0:2103")

	s := grpc.NewServer()
	dialout.RegisterGRPCMdtDialoutServer(s, &server{})

	s.Serve(listen)

}

ここで注意が必要なのは、google.golang.org/protobuf/protoではなく、 github.com/golang/protobuf/proto を使わなければならないということろである。エディタの補完では前者になってしまうことがあるので、明示的に後者を指定すること。

あとは、telem.DataGpbkvをいい感じにパースして、目的にデータストアに投げればいい(ここはsensor pathに応じて異なる処理が必要になるので割愛する)。

ちなみに、gRPC serverと接続しているとき、XRv側では以下のようにステータスを確認できる。

RP/0/RP0/CPU0:xrv#show telemetry model-driven subscription PerfStats 
Sun Sep 20 00:17:49.074 JST
Subscription:  PerfStats
-------------
  State:       ACTIVE
  Source Interface:       MgmtEth0_RP0_CPU0_0(Up 0x60000000)
  Sensor groups:
  Id: Perf
    Sample Interval:      5000 ms
    Sensor Path:          Cisco-IOS-XR-wdsysmon-fd-oper:system-monitoring/cpu-utilization
    Sensor Path State:    Resolved

  Destination Groups:
  Group Id: gollector
    Destination IP:       192.168.1.100
    Destination Port:     2103
    Encoding:             self-describing-gpb
    Transport:            grpc
    State:                Active
    No TLS                
    Total bytes sent:     0
    Total packets sent:   0

  Collection Groups:
  ------------------
    Id: 1
    Sample Interval:      5000 ms
    Encoding:             self-describing-gpb
    Num of collection:    0
    Collection time:      Min:     0 ms Max:     0 ms
    Total time:           Min:     0 ms Avg:     0 ms Max:     0 ms
    Total Deferred:       0
    Total Send Errors:    0
    Total Send Drops:     0
    Total Other Errors:   0
    No data Instances:    0
    Last Collection Start:2020-09-20 00:17:49.516345779 +0900
    Last Collection End:  2020-09-20 00:17:44.511967965 +0900
    Sensor Path:          Cisco-IOS-XR-wdsysmon-fd-oper:system-monitoring/cpu-utilization

以上