SONiCのtelemetry機能で取得できる情報

# はじめに

[SONiC](https://azure.github.io/SONiC/)のtelemetryですが、dial-in (server) と dial-out (client) があります。ここではdial-inについて解説します。簡単に言えば、外からSONiCに対して情報取得を要求し、SONiCが応答するという動作になります。動かしながら説明していきたいと思います。

なお、環境はSONiC-VS on GNS3です。

# 準備

何も設定していなくても標準でtelemetry serviceが動作しています。何も設定してなければどこからでも情報取得ができます。がばがばです。証明書を用意してクライアント認証を有効にすることでセキュアにできますが、今回は情報取得の解説がメインなので省略します。

よってSONiC側は設定不要ですが、情報取得のためのクライアントプログラムが必要です。

## クライアントプログラム `gnmi_get`のインストール

SONiCのtelemetry機能はgRPCによって情報取得するものですが、[公式ドキュメント](https://github.com/Azure/sonic-telemetry/blob/master/doc/grpc_telemetry.md)でも言及されている下記のプログラムを使って情報取得します。(検索すると[Google謹製のgnmi_get](https://github.com/google/gnxi/tree/master/gnmi_get)が見つかりますが、コマンドラインパラメータが異なるようで使えません、ご注意ください)

```
$ go get github.com/jipanyang/gnxi/gnmi_get
```


## `gnmi_get`の動作確認

以降のサンプル出力において、`192.168.1.34`はターゲットのSONiCのマネジメントポートにつけられたIPアドレスです。`8080`は無指定時のポート番号になります。

```
$ gnmi_get -xpath_target COUNTERS_DB -xpath COUNTERS_PORT_NAME_MAP -target_addr 192.168.1.34:8080 -insecure true
== getRequest:
prefix: <
  target: "COUNTERS_DB"
>
path: <
  elem: <
    name: "COUNTERS_PORT_NAME_MAP"
  >
>
encoding: JSON_IETF

== getResponse:
notification: <
  timestamp: 1610990669317028240
  prefix: <
    target: "COUNTERS_DB"
  >
  update: <
    path: <
      elem: <
        name: "COUNTERS_PORT_NAME_MAP"
      >
    >
    val: <
      json_ietf_val: "{\"Ethernet0\":\"oid:0x1000000000002\",\"Ethernet100\":\"oid:0x100000000001b\",\"Ethernet104\":\"oid:0x100000000001c\",\"Ethernet108\":\"oid:0x100000000001d\",\"Ethernet112\":\"oid:0x100000000001e\",\"Ethernet116\":\"oid:0x100000000001f\",\"Ethernet12\":\"oid:0x1000000000005\",\"Ethernet120\":\"oid:0x1000000000020\",\"Ethernet124\":\"oid:0x1000000000021\",\"Ethernet16\":\"oid:0x1000000000006\",\"Ethernet20\":\"oid:0x1000000000007\",\"Ethernet24\":\"oid:0x1000000000008\",\"Ethernet28\":\"oid:0x1000000000009\",\"Ethernet32\":\"oid:0x100000000000a\",\"Ethernet36\":\"oid:0x100000000000b\",\"Ethernet4\":\"oid:0x1000000000003\",\"Ethernet40\":\"oid:0x100000000000c\",\"Ethernet44\":\"oid:0x100000000000d\",\"Ethernet48\":\"oid:0x100000000000e\",\"Ethernet52\":\"oid:0x100000000000f\",\"Ethernet56\":\"oid:0x1000000000010\",\"Ethernet60\":\"oid:0x1000000000011\",\"Ethernet64\":\"oid:0x1000000000012\",\"Ethernet68\":\"oid:0x1000000000013\",\"Ethernet72\":\"oid:0x1000000000014\",\"Ethernet76\":\"oid:0x1000000000015\",\"Ethernet8\":\"oid:0x1000000000004\",\"Ethernet80\":\"oid:0x1000000000016\",\"Ethernet84\":\"oid:0x1000000000017\",\"Ethernet88\":\"oid:0x1000000000018\",\"Ethernet92\":\"oid:0x1000000000019\",\"Ethernet96\":\"oid:0x100000000001a\"}"
    >
  >
>

$
```

# 取得できる情報

`gnmi_get`の`-xpath_target`と`-xpath`を使って取得する情報を指定します。

## `-xpath_target`に何が指定できるのか

動作確認の例を見ていただければわかると思いますが、SONiCが抱えているRedisデータベース名です。`src/sonic-swss-common/common/schema.h`から引っ張り出してます。

- `APPL_DB`
- `ASIC_DB`
- `COUNTERS_DB`
- `LOGLEVEL_DB`
- `CONFIG_DB`
- `PFC_WD_DB`
- `FLEX_COUNTER_DB`
- `STATE_DB`

DB定義には下記もありますが、テーブル内容が不明のため以降では言及しません。

- `SNMP_OVERLAY_DB`
- `RESTAPI_DB`
- `GB_ASIC_DB`
- `GB_COUNTERS_DB`
- `GB_FLEX_COUNTER_DB`
- `CHASSIS_APP_DB`
- `CHASSIS_STATE_DB`

## `-xpath`なにが指定できるのか

DBのテーブル名以降を指定できます。テーブル名は下記です。めちゃくちゃ多いので折りたたんであります。テーブル名のみを指定するとテーブル全体を、個別の項目を指定すればそこだけ取得できます。

<details><summary>APPL_DB</summary><div>

- `PORT_TABLE`
- `GEARBOX_TABLE`
- `VLAN_TABLE`
- `VLAN_MEMBER_TABLE`
- `LAG_TABLE`
- `LAG_MEMBER_TABLE`
- `INTF_TABLE`
- `NEIGH_TABLE`
- `ROUTE_TABLE`
- `TUNNEL_DECAP_TABLE`
- `FDB_TABLE`
- `PFC_WD_TABLE`
- `SWITCH_TABLE`
- `COPP_TABLE`
- `VRF_TABLE`
- `VNET_TABLE`
- `VNET_ROUTE_TABLE`
- `VNET_ROUTE_TUNNEL_TABLE`
- `VXLAN_VRF_TABLE`
- `VXLAN_TUNNEL_MAP_TABLE`
- `VXLAN_TUNNEL_TABLE`
- `VXLAN_FDB_TABLE`
- `VXLAN_REMOTE_VNI_TABLE`
- `VXLAN_EVPN_NVO_TABLE`
- `SUPPRESS_VLAN_NEIGH_TABLE`
- `PASS_THROUGH_ROUTE_TABLE`
- `ACL_TABLE_TABLE`
- `ACL_RULE_TABLE`
- `SFLOW_TABLE`
- `SFLOW_SESSION_TABLE`
- `SFLOW_SAMPLE_RATE_TABLE`
- `NAT_TABLE`
- `NAPT_TABLE`
- `NAT_TWICE_TABLE`
- `NAPT_TWICE_TABLE`
- `NAT_GLOBAL_TABLE`
- `NAPT_POOL_IP_TABLE`
- `NAT_DNAT_POOL_TABLE`
- `STP_VLAN_TABLE`
- `STP_VLAN_PORT_TABLE`
- `STP_VLAN_INSTANCE_TABLE`
- `STP_PORT_TABLE`
- `STP_PORT_STATE_TABLE`
- `STP_FASTAGEING_FLUSH_TABLE`
- `STP_BPDU_GUARD_TABLE`
- `MCLAG_FDB_TABLE`
- `ISOLATION_GROUP_TABLE`

</div></details>
<details><summary>ASIC_DB</summary><div>

- ASIC_TEMPERATURE_INFO

</div></details>
<details><summary>COUNTER_DB</summary><div>

- `COUNTERS_PORT_NAME_MAP`
- `COUNTERS_LAG_NAME_MAP`
- `COUNTERS`
- `COUNTERS_QUEUE_NAME_MAP`
- `COUNTERS_QUEUE_PORT_MAP`
- `COUNTERS_QUEUE_INDEX_MAP`
- `COUNTERS_QUEUE_TYPE_MAP`
- `COUNTERS_PG_NAME_MAP`
- `COUNTERS_PG_PORT_MAP`
- `COUNTERS_PG_INDEX_MAP`
- `COUNTERS_RIF_TYPE_MAP`
- `COUNTERS_RIF_NAME_MAP`
- `CRM`
- `COUNTERS_BUFFER_POOL_NAME_MAP`
- `COUNTERS_SWITCH_NAME_MAP`
- `COUNTERS_MACSEC_NAME_MAP`
- `COUNTERS_DEBUG_NAME_PORT_STAT_MAP`
- `COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP`
- `COUNTERS_NAT`
- `COUNTERS_NAPT`
- `COUNTERS_TWICE_NAT`
- `COUNTERS_TWICE_NAPT`
- `COUNTERS_GLOBAL_NAT`
- `PERIODIC_WATERMARKS`
- `PERSISTENT_WATERMARKS`
- `USER_WATERMARKS`
- `RATES`

</div></details>
<details><summary>LOGLEVEL_DB</summary><div>

スキーマには記述がありましたが、SONiC-VSでは情報取得できませんでした。

- `DAEMON_TABLE`
- `LOGLEVEL`
- `LOGOUTPUT`

</div></details>
<details><summary>FLEX_COUNTER_DB</summary><div>

- `FLEX_COUNTER_TABLE`
- `PORT_COUNTER_ID_LIST`
- `PORT_DEBUG_COUNTER_ID_LIST`
- `QUEUE_COUNTER_ID_LIST`
- `QUEUE_ATTR_ID_LIST`
- `BUFFER_POOL_COUNTER_ID_LIST`
- `PFC_WD_STATE_TABLE`
- `PORT_COUNTER_ID_LIST`
- `QUEUE_COUNTER_ID_LIST`
- `QUEUE_ATTR_ID_LIST`
- `PG_COUNTER_ID_LIST`
- `PG_ATTR_ID_LIST`
- `RIF_COUNTER_ID_LIST`
- `SWITCH_DEBUG_COUNTER_ID_LIST`
- `MACSEC_SA_ATTR_ID_LIST`
- `PLUGIN_TABLE`
- `LUA_PLUGIN_TYPE`
- `SAI_OBJECT_TYPE`
- `POLL_INTERVAL`
- `STATS_MODE`
- `STATS_MODE_READ`
- `STATS_MODE_READ_AND_CLEAR`
- `QUEUE_PLUGIN_LIST`
- `PORT_PLUGIN_LIST`
- `RIF_PLUGIN_LIST`
- `PG_PLUGIN_LIST`
- `BUFFER_POOL_PLUGIN_LIST`
- `FLEX_COUNTER_STATUS`
- `FLEX_COUNTER_GROUP_TABLE`

</div></details>
<details><summary>CONFIG_DB</summary><div>

- `PORT`
- `CABLE_LENGTH`
- `GEARBOX`
- `INTERFACE`
- `LOOPBACK_INTERFACE`
- `MGMT_INTERFACE`
- `PORTCHANNEL_INTERFACE`
- `VLAN_INTERFACE`
- `VLAN_SUB_INTERFACE`
- `ASIC_SENSORS`
- `PORTCHANNEL`
- `PORTCHANNEL_MEMBER`
- `VLAN`
- `VLAN_MEMBER`
- `FDB`
- `SWITCH`
- `VRF`
- `CRM`
- `DHCP_SERVER`
- `NTP_SERVER`
- `SYSLOG_SERVER`
- `BGP_NEIGHBOR`
- `BGP_INTERNAL_NEIGHBOR`
- `BGP_MONITORS`
- `BGP_PEER_RANGE`
- `DEVICE_METADATA`
- `DEVICE_NEIGHBOR`
- `DEVICE_NEIGHBOR_METADATA`
- `MIRROR_SESSION`
- `ACL_TABLE`
- `ACL_RULE`
- `PFC_WD`
- `FLEX_COUNTER_TABLE`
- `WATERMARK_TABLE`
- `PFC_PRIORITY_TO_PRIORITY_GROUP_MAP`
- `TC_TO_PRIORITY_GROUP_MAP`
- `MAP_PFC_PRIORITY_TO_QUEUE`
- `TC_TO_QUEUE_MAP`
- `DSCP_TO_TC_MAP`
- `SCHEDULER`
- `PORT_QOS_MAP`
- `WRED_PROFILE`
- `QUEUE`
- `DOT1P_TO_TC_MAP`
- `BUFFER_POOL`
- `BUFFER_PROFILE`
- `BUFFER_QUEUE`
- `BUFFER_PG`
- `BUFFER_PORT_INGRESS_PROFILE_LIST`
- `BUFFER_PORT_EGRESS_PROFILE_LIST`
- `DEFAULT_LOSSLESS_BUFFER_PARAMETER`
- `POLICER`
- `WARM_RESTART`
- `VXLAN_TUNNEL`
- `VXLAN_TUNNEL_MAP`
- `VXLAN_EVPN_NVO`
- `VNET`
- `NEIGH`
- `SUPPRESS_VLAN_NEIGH`
- `VNET_ROUTE`
- `VNET_ROUTE_TUNNEL`
- `PASS_THROUGH_ROUTE_TABLE`
- `SFLOW`
- `SFLOW_SESSION`
- `DEBUG_COUNTER`
- `DEBUG_COUNTER_DROP_REASON`
- `STATIC_NAT`
- `STATIC_NAPT`
- `NAT_POOL`
- `NAT_BINDINGS`
- `NAT_GLOBAL`
- `STP`
- `STP_VLAN`
- `STP_VLAN_PORT`
- `STP_PORT`
- `MCLAG_DOMAIN`
- `MCLAG_INTERFACE`
- `MCLAG_UNIQUE_IP`
- `PORT_STORM_CONTROL`
- `RATES`
- `FEATURE`
- `COPP_TRAP`
- `COPP_GROUP`
- `FG_NHG`
- `FG_NHG_PREFIX`
- `FG_NHG_MEMBER`
- `MUX_CABLE`
- `PEER_SWITCH`
- `TUNNEL`
- `SYSTEM_PORT`
- `VOQ_INBAND_INTERFACE`
- `MACSEC_PROFILE`
- `SYSTEM_INTERFACE`
- `SYSTEM_NEIGH`
- `CHASSIS_MODULE`

</div></details>
<details><summary>STATE_DB</summary><div>

- `SWITCH_CAPABILITY_TABLE`
- `PORT_TABLE`
- `LAG_TABLE`
- `VLAN_TABLE`
- `VLAN_MEMBER_TABLE`
- `INTERFACE_TABLE`
- `FDB_TABLE`
- `WARM_RESTART_TABLE`
- `WARM_RESTART_ENABLE_TABLE`
- `VRF_TABLE`
- `VRF_OBJECT_TABLE`
- `MGMT_PORT_TABLE`
- `NEIGH_RESTORE_TABLE`
- `MIRROR_SESSION_TABLE`
- `VXLAN_TABLE`
- `VXLAN_TUNNEL_TABLE`
- `SUPPRESS_VLAN_NEIGH_TABLE`
- `BGP_STATE_TABLE`
- `DEBUG_COUNTER_CAPABILITIES`
- `NAT_RESTORE_TABLE`
- `MCLAG_TABLE`
- `MCLAG_LOCAL_INTF_TABLE`
- `MCLAG_REMOTE_INTF_TABLE`
- `MCLAG_REMOTE_FDB_TABLE`
- `STP_TABLE`
- `COPP_GROUP_TABLE`
- `COPP_TRAP_TABLE`
- `FG_ROUTE_TABLE`
- `MUX_CABLE_TABLE`
- `HW_MUX_CABLE_TABLE`
- `SYSTEM_NEIGH_TABLE`
- `MACSEC_PORT_TABLE`
- `MACSEC_INGRESS_SC_TABLE`
- `MACSEC_INGRESS_SA_TABLE`
- `MACSEC_EGRESS_SC_TABLE`
- `MACSEC_EGRESS_SA_TABLE`
- `ASIC_TABLE`
- `BUFFER_MAX_PARAM_TABLE`
- `PERIPHERAL_TABLE`
- `PORT_PERIPHERAL_TABLE`
- `BUFFER_POOL_TABLE`
- `BUFFER_PROFILE_TABLE`

</div></details>

# 取得例

CONFIG_DBから`PORT`テーブルの`Ethernet0`の内容を取得してみます。

さすがに`gnmi_get`の出力のままでは見づらいので、スクリプトで必要な部分を取り出して整形することにします。

https://gist.github.com/iMasaruOki/8562628d5240491afa6d66129c91bda0

実行すると、下記のようになります。

```json
$ ./telemetry_get.sh 192.168.1.34:8080 CONFIG_DB PORT/Ethernet0
{
  "admin_status": "up",
  "alias": "fortyGigE0/0",
  "index": "0",
  "lanes": "25,26,27,28",
  "mtu": "9100",
  "speed": "40000"
}
$ 
```

なお、テーブルが存在しない場合取得に失敗します。たとえばVLANを設定していない場合、下記のようになります。(設定すれば取得できます)

```
$ ./telemetry_get.sh 192.168.1.34:8080 CONFIG_DB VLAN
F0118 14:15:20.090200   48481 gnmi_get.go:137] Get Failed: rpc error: code = Not Found desc = Failed to find CONFIG_DB VLAN <nil> []
$
```

# OpenConfig?

サポートしているようです。確認のため、`gnmi_capabilities`を使ってみます。

```
$ go get github.com/google/gnxi/gnmi_capabilities
```

実行結果は下記。

```
$ gnmi_capabilities -target_addr 192.168.1.34:8080 -target_name sonic -insecure
== CapabilitiesResponse:
 supported_models: <
  name: "openconfig-acl"
  organization: "OpenConfig working group"
  version: "1.0.2"
>
supported_models: <
  name: "openconfig-interfaces"
  organization: "OpenConfig working group"
  version: "1.0.2"
>
supported_models: <
  name: "openconfig-lldp"
  organization: "OpenConfig working group"
  version: "1.0.2"
>
supported_models: <
  name: "openconfig-platform"
  organization: "OpenConfig working group"
  version: "1.0.2"
>
supported_models: <
  name: "openconfig-system"
  organization: "OpenConfig working group"
  version: "1.0.2"
>
supported_encodings: JSON
supported_encodings: JSON_IETF
gNMI_version: "0.7.0"

$ 

```

実際に取得してみます。`-xpath_target`に`openconfig`を指定し、`-xpath`にOpenConfigのpathを書きます。


```json
$ ./telemetry_get.sh 192.168.1.34:8080 openconfig /openconfig-interfaces:interfaces/interface[name=Ethernet0]
{
  "openconfig-interfaces:interface": [
    {
      "config": {
        "description": "",
        "mtu": 9100,
        "name": "Ethernet0"
      },
      "name": "Ethernet0",
      "openconfig-if-ethernet:ethernet": {
        "config": {
          "port-speed": "openconfig-if-ethernet:SPEED_40GB"
        },
        "state": {
          "port-speed": "openconfig-if-ethernet:SPEED_40GB"
        }
      },
      "state": {
        "admin-status": "UP",
        "counters": {
          "in-broadcast-pkts": "0",
          "in-discards": "0",
          "in-errors": "0",
          "in-multicast-pkts": "0",
          "in-octets": "0",
          "in-pkts": "0",
          "in-unicast-pkts": "0",
          "out-broadcast-pkts": "0",
          "out-discards": "0",
          "out-errors": "0",
          "out-multicast-pkts": "0",
          "out-octets": "0",
          "out-pkts": "0",
          "out-unicast-pkts": "0"
        },
        "description": "",
        "ifindex": 0,
        "mtu": 9100,
        "name": "Ethernet0",
        "oper-status": "DOWN"
      },
      "subinterfaces": {
        "subinterface": [
          {
            "index": 0
          }
        ]
      }
    }
  ]
}
$ 
```

標準に則った(OSが違っても同じフォーマットの)出力が得られますので、情報量に不足がなければOpenConfigフォーマットを使ったほうがいいと思います。が、capabilitiesの結果からわかるとおりいろいろ足りないので、今後に期待というところです。

# おわりに

設定もできると思いますが、今回は割愛しました。いつか試してみたいと思います。