|
| 1 | +## Overview |
| 2 | + |
| 3 | +The OpenConfig gNMI service typically operates on a **dial-in model**, where the client/collector initiates the |
| 4 | +connection with the gNMI server (running on switch). |
| 5 | + |
| 6 | +However, this model has limitations if the client cannot connect to the server, due to factors like firewalls blocking |
| 7 | +inbound connections or the server being behind a router implementing NAT. |
| 8 | + |
| 9 | +To address such issues, a **dial-out model** is more advantageous, allowing the server to instantiate a connection |
| 10 | +in reverse to the client/collector. |
| 11 | + |
| 12 | +| Dial-in | Dial-out | |
| 13 | +|:-------:| :-------:| |
| 14 | +||| |
| 15 | + |
| 16 | +This page provides examples for configuring and using gNMI Dial-out on EOS, which can be achieved using two methods: |
| 17 | + |
| 18 | +1. gNMIReverse client |
| 19 | +2. gNMI Dial-out via gRPC Tunnel |
| 20 | + |
| 21 | +### gNMIReverse client |
| 22 | + |
| 23 | +gNMIReverse client is a Dial-Out gRPC service |
| 24 | +([GitHub](https://github.com/aristanetworks/goarista/tree/master/cmd/gnmireverse_client)) |
| 25 | +that reverses the direction of the dial for gNMI Subscriptions, where the gNMIReverse client running along with |
| 26 | +gNMI target (on the switch) sends data to the gNMIReverse Server. |
| 27 | + |
| 28 | +#### Building the gNMIReverse binaries |
| 29 | + |
| 30 | +:information_source: [Go](https://go.dev/doc/install) is required for compiling the |
| 31 | + gNMIReverse client and server binaries. |
| 32 | + |
| 33 | +Once Go is set up, run the following commands to compile the `gnmireverse_client` binary, |
| 34 | +which will be installed on the switch. |
| 35 | + |
| 36 | +For 32-bit EOS: |
| 37 | + |
| 38 | +```shell |
| 39 | +GOOS=linux GOARCH=386 CGO_ENABLED=0 go install github.com/aristanetworks/goarista/cmd/gnmireverse_client@latest |
| 40 | +``` |
| 41 | + |
| 42 | +For 64-bit EOS: |
| 43 | + |
| 44 | +```shell |
| 45 | +GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go install github.com/aristanetworks/goarista/cmd/gnmireverse_client@latest |
| 46 | +``` |
| 47 | + |
| 48 | +This will build the `gnmireverse_client` binary in the `$GOPATH/bin` or `$GOPATH/bin/linux_386` or |
| 49 | +`$GOPATH/bin/linux_amd64` directory. |
| 50 | + |
| 51 | +:information_source: **Note**: To find the architecture of EOS, use the following command: |
| 52 | + |
| 53 | +```text |
| 54 | +switch# show version | include Arch |
| 55 | +Architecture: i686 <<-- 32 bit architecture |
| 56 | +
|
| 57 | +switch# show version | include Arch |
| 58 | +Architecture: x86_64 <<-- 64 bit architecture |
| 59 | +``` |
| 60 | + |
| 61 | +Copy the client binary to `/mnt/flash` directory on the switch, using scp or WinSCP |
| 62 | + |
| 63 | +```shell |
| 64 | +scp $GOPATH/bin/linux_amd64/gnmireverse_client <USERNAME>@<switch-MGMT-IP>:/mnt/flash/ |
| 65 | +``` |
| 66 | + |
| 67 | +To build the collector binary implementing a gNMIReverse server, use the following command: |
| 68 | + |
| 69 | +```shell |
| 70 | +go install github.com/aristanetworks/goarista/cmd/gnmireverse_server@latest |
| 71 | +``` |
| 72 | + |
| 73 | +:information_source: **Note**: Starting from release EOS-4.28.1 `gnmireverse_client` and `gnmireverse_server` |
| 74 | +binaries are also bundled with Octa on EOS. |
| 75 | + |
| 76 | +```text |
| 77 | +leaf2# show version | include Soft|Arch |
| 78 | +Software image version: 4.28.1F-27567367.4281F |
| 79 | +Architecture: x86_64 |
| 80 | +
|
| 81 | +leaf2# bash which gnmireverse_client; which gnmireverse_server |
| 82 | +/usr/bin/gnmireverse_client |
| 83 | +/usr/bin/gnmireverse_server |
| 84 | +
|
| 85 | +leaf2# bash gnmireverse_client --help |
| 86 | +Usage of gnmireverse_client: |
| 87 | + -clientSideWildcard |
| 88 | + forces client side wildcard resolution (default true) |
| 89 | + -collector_addr string |
| 90 | + Address of collector in the form of [<vrf-name>/]host:port. |
| 91 | + The host portion must be enclosed in square brackets if it is a literal IPv6 address. |
| 92 | + For example, -collector_addr mgmt/[::1]:1234 |
| 93 | +<--snipped--> |
| 94 | +``` |
| 95 | + |
| 96 | +#### gNMIReverse client configuration |
| 97 | + |
| 98 | +Before proceeding, the gNMI server needs to be enabled on the switch, |
| 99 | + refer to [this](../../../configuration/openconfig.md#gnmi) |
| 100 | +section for sample configuration. |
| 101 | + |
| 102 | +:information_source: **Note**: If subscribing to `eos_native` paths, Octa also needs to be enabled. |
| 103 | + Refer to [this](../../../configuration/openconfig.md#octa) section for more details on Octa. |
| 104 | + |
| 105 | +To see the full list of options and documentation, run `gnmireverse_client --help`. |
| 106 | + Some of the important flags are explained |
| 107 | +[in this section](https://github.com/aristanetworks/goarista/tree/master/cmd/gnmireverse_client#options). |
| 108 | + |
| 109 | +With gNMI server enabled, gNMIReverse client daemon can be configured as follows: |
| 110 | + |
| 111 | +> Default VRF |
| 112 | +
|
| 113 | +```text |
| 114 | +! |
| 115 | +daemon gnmireverse |
| 116 | + exec /mnt/flash/gnmireverse_client -target_value=leaf1 |
| 117 | + -collector_addr=192.185.128.100:30000 -collector_tls=false |
| 118 | + -sample /interfaces/interface[name=Ethernet1]/state/counters@30s |
| 119 | + -subscribe system/memory/state/free |
| 120 | + no shutdown |
| 121 | +! |
| 122 | +``` |
| 123 | + |
| 124 | +:information_source: **Note**: When configuring on EOS CLI, combine the wrapped exec command flags into a single line. |
| 125 | + |
| 126 | +In above example both gNMI server and collector are in *default VRF*: |
| 127 | + |
| 128 | +- Target is the default Unix domain socket gNMI server, which does not require authentication. |
| 129 | +- The collector/client `192.185.128.100` is reachable via the default VRF on port 30000. |
| 130 | +- Counters for Interface Ethernet1 are sampled at a 30-second interval |
| 131 | +- Subscription is done for OpenConfig path `system/memory/state/free`. |
| 132 | + |
| 133 | +> Non default VRF |
| 134 | +
|
| 135 | +```text |
| 136 | +! |
| 137 | +daemon gnmireverse |
| 138 | + exec /mnt/flash/gnmireverse_client |
| 139 | + -target_addr=MGMT/172.100.100.2:6030 -target_value=spine1 |
| 140 | + -username=admin -password=admin |
| 141 | + -collector_addr=MGMT/192.185.128.100:30000 -collector_tls=false |
| 142 | + -get_sample_interval=10s -get interfaces/interface/state/oper-status |
| 143 | + -get components/component/state/memory -get eos_native:/Eos/image |
| 144 | + no shutdown |
| 145 | +! |
| 146 | +``` |
| 147 | + |
| 148 | +:information_source: **Note**: When configuring on EOS CLI, combine the wrapped exec command flags into a single line. |
| 149 | + |
| 150 | +In above example both gNMI server and collector are in *non default VRF* named `MGMT`: |
| 151 | + |
| 152 | +- Target is the Ma1 interface IP in the `MGMT` VRF, utilizing the authentication credentials `admin/admin`. |
| 153 | +- The collector/client `192.185.128.100` is reachable via the `MGMT` VRF on port 30000. |
| 154 | +- A gNMI Get request is issued every 10 seconds to retrieve |
| 155 | + - all interfaces operational status |
| 156 | + - system memory |
| 157 | + - EOS image information from the EOS native path. |
| 158 | + |
| 159 | +#### Running the gNMIReverse Server |
| 160 | + |
| 161 | +Using the following command, the gNMIReverse server is started on the collector |
| 162 | + |
| 163 | +```shell |
| 164 | +gnmireverse_server -tls=false -addr=192.185.128.100:30000 -debug -1 |
| 165 | +``` |
| 166 | + |
| 167 | +<details><summary>Reveal output</summary> |
| 168 | +<p> |
| 169 | +```shell |
| 170 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 171 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/interfaces/interface[name=Ethernet1]/state/oper-status val=UP |
| 172 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 173 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/interfaces/interface[name=Ethernet2]/state/oper-status val=UP |
| 174 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 175 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/interfaces/interface[name=Management1]/state/oper-status val=UP |
| 176 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 177 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 178 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/displayVersion val=4.35.0F-44178984.4350F |
| 179 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 180 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/serialNum val=33b708fe-8b04-48db-bb84-7f77a6b3cc66 |
| 181 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 182 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/version val=4.35.0F |
| 183 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 184 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/arch val=x86_64 |
| 185 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 186 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/blessed val=false |
| 187 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 188 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/flavor val=cEOS |
| 189 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 190 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/name val=image |
| 191 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 192 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/release val=44178984.4350F |
| 193 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 194 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/variant val=US |
| 195 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 196 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/buildHost val=dhcp-224-68-53.sjc.aristanetworks.com |
| 197 | +client=172.100.100.2:34152 res=get n=0 rx_time=2025-10-21T05:42:21.103533957Z notif_time=2025-10-21T05:42:21.102427813Z |
| 198 | + last_rx_ago=0s last_notif_ago=0s latency=0s path=/Eos/image/buildTime val=1759431887000000000 |
| 199 | +``` |
| 200 | + |
| 201 | +</p> |
| 202 | +</details> |
| 203 | + |
| 204 | +#### Troubleshooting |
| 205 | + |
| 206 | +Use the following command to check the logs for configured gnmireverse daemon: |
| 207 | + |
| 208 | +```shell |
| 209 | +show agent gnmireverse logs |
| 210 | +``` |
| 211 | + |
| 212 | +### gNMI Dial-out via gRPC Tunnel |
| 213 | + |
| 214 | +Another way for using gNMI Dial-out involves leveraging the |
| 215 | +gRPC Tunnel [specification](https://github.com/openconfig/grpctunnel/blob/main/doc/grpctunnel_design.md). |
| 216 | +For more details on this feature |
| 217 | + refer to [EOS TOI](https://www.arista.com/en/support/toi/eos-4-27-0f/14851-gnmi-dial-out-via-grpc-tunnel). |
| 218 | + |
| 219 | +In this method the gNMI Dial-out sequence involves the following steps: |
| 220 | + |
| 221 | +- The gRPC tunnel client on the switch initiates a connection, dialing out to the collector. |
| 222 | +- The collector, running a gRPC tunnel server, listens for and accepts the incoming tunnel request. |
| 223 | +- A secure gRPC tunnel session is established between the switch and the collector. |
| 224 | +- The collector sends a gNMI request to the gNMI server, via the established gRPC tunnel. |
| 225 | +- The gNMI server returns the corresponding responses to the collector via the same tunnel. |
| 226 | + |
| 227 | + |
| 228 | + |
| 229 | +#### gRPC tunnel client configuration |
| 230 | + |
| 231 | +Under `management api gnmi` use the `transport grpc-tunnel <name>` to configure a gRPC tunnel client. |
| 232 | + |
| 233 | +```text |
| 234 | +! |
| 235 | +management api gnmi |
| 236 | + transport grpc-tunnel tunnel1 |
| 237 | + vrf MGMT |
| 238 | + destination 192.185.128.100 port 30000 |
| 239 | + local interface Management1 port 50000 |
| 240 | + target spine1 |
| 241 | + provider eos-native |
| 242 | +! |
| 243 | +``` |
| 244 | + |
| 245 | +The above example configures a gRPC tunnel client named `tunnel1` (*multiple such clients can be configured*): |
| 246 | + |
| 247 | +- The client is dialing out from non default VRF `MGMT`, this config is optional. |
| 248 | +- The collector is running on 192.185.128.100 on port 30000, **this is a required configuration to start the client**. |
| 249 | +- The client dials out using the Management1 interface IP and port 50000, this config is optional. |
| 250 | +- The target ID is `spine1`, this is a user defined string, used to identify the switch used in the gRPC tunnel |
| 251 | + establishment and **is a required configuration to start the client**. |
| 252 | +- The optional tunnel and gNMI server SSL/TLS profiles can also be configured. |
| 253 | +- For more information on configuration options refer to the feature |
| 254 | + [TOI](https://www.arista.com/en/support/toi/eos-4-27-0f/14851-gnmi-dial-out-via-grpc-tunnel). |
| 255 | + |
| 256 | +Following command will show the the status of gRPC tunnel clients: |
| 257 | + |
| 258 | +```text |
| 259 | +switch# show management api gnmi |
| 260 | +Octa: enabled |
| 261 | +
|
| 262 | +Transport gRPC-tunnel: tunnel1 |
| 263 | +Status: tunnel connection established |
| 264 | +Enabled: yes |
| 265 | +VRF: MGMT |
| 266 | +Destination address: 192.185.128.100, port 30000 |
| 267 | +Local interface: Management1 |
| 268 | +Local address: 172.100.100.2, port 50000 |
| 269 | +Target ID: spine1 |
| 270 | +``` |
| 271 | + |
| 272 | +#### gNMIC Tunnel Server |
| 273 | + |
| 274 | +This demo will be using the tunnel server which is available as part of the |
| 275 | + [gNMIc](https://gnmic.openconfig.net/user_guide/tunnel_server/) tool. |
| 276 | +Following `gnmic.yml` configuration file is used to specify all the command line flags by means of a single file. |
| 277 | + |
| 278 | +```yaml |
| 279 | +--- |
| 280 | +insecure: true |
| 281 | +log: true |
| 282 | +username: admin |
| 283 | +password: admin |
| 284 | + |
| 285 | +subscriptions: |
| 286 | + system-info: |
| 287 | + mode: once |
| 288 | + paths: |
| 289 | + - '/system/state/software-version' |
| 290 | + - '/system/state/hostname' |
| 291 | + port-stats: |
| 292 | + paths: |
| 293 | + - 'interfaces/interface[name=Management1]/state/counters/in-octets' |
| 294 | + - 'interfaces/interface[name=Management1]/state/counters/out-octets' |
| 295 | + stream_mode: on-change |
| 296 | + mem-stats: |
| 297 | + sample-interval: 5s |
| 298 | + paths: |
| 299 | + - 'eos_native:/Kernel/proc/meminfo' |
| 300 | + cpu-stats: |
| 301 | + sample-interval: 5s |
| 302 | + paths: |
| 303 | + - 'eos_native:/Kernel/proc/cpu/utilization/total' |
| 304 | + |
| 305 | +tunnel-server: |
| 306 | + address: ":30000" |
| 307 | + targets: |
| 308 | + - id: spine1 |
| 309 | + config: |
| 310 | + subscriptions: |
| 311 | + - system-info |
| 312 | + - mem-stats |
| 313 | + - port-stats |
| 314 | +``` |
| 315 | +
|
| 316 | +Using the following command, the tunnel server is started on the collector: |
| 317 | +
|
| 318 | +```shell |
| 319 | +gnmic --config ./gnmic.yml --use-tunnel-server subscribe |
| 320 | +``` |
| 321 | + |
| 322 | +<details><summary>Reveal output</summary> |
| 323 | +<p> |
| 324 | +```shell |
| 325 | +2025/10/21 17:48:14.648027 [gnmic] tunnel server discovered target {ID:spine1 Type:GNMI_GNOI} |
| 326 | +2025/10/21 17:48:14.648373 [gnmic] starting target "spine1" listener |
| 327 | +2025/10/21 17:48:14.648405 [gnmic] queuing target "spine1" |
| 328 | +2025/10/21 17:48:14.648417 [gnmic] subscribing to target: "spine1" |
| 329 | +2025/10/21 17:48:14.648896 [gnmic] target "spine1" gNMI client created |
| 330 | +2025/10/21 17:48:14.648964 [gnmic] dialing tunnel connection for tunnel target "spine1" |
| 331 | +2025/10/21 17:48:14.648904 [gnmic] sending gNMI SubscribeRequest: |
| 332 | +subscribe='subscribe:{subscription:{path:{elem:{name:"system"} elem:{name:"state"} elem:{name:"software-version"}}} |
| 333 | + subscription:{path:{elem:{name:"system"} elem:{name:"state"} elem:{name:"hostname"}}} mode:ONCE}', mode='ONCE', |
| 334 | + encoding='JSON', to spine1 |
| 335 | +2025/10/21 17:48:14.649543 [gnmic] sending gNMI SubscribeRequest: |
| 336 | + subscribe='subscribe:{subscription:{path:{origin:"eos_native" elem:{name:"Kernel"} elem:{name:"proc"} |
| 337 | + elem:{name:"meminfo"}} sample_interval:5000000000}}', mode='STREAM', encoding='JSON', to spine1 |
| 338 | +{ |
| 339 | + "source": "spine1", |
| 340 | + "subscription-name": "system-info", |
| 341 | + "timestamp": 1761025184960219526, |
| 342 | + "time": "2025-10-21T11:09:44.960219526+05:30", |
| 343 | + "updates": [ |
| 344 | + { |
| 345 | + "Path": "system/state/software-version", |
| 346 | + "values": { |
| 347 | + "system/state/software-version": "4.35.0F-44178984.4350F" |
| 348 | + } |
| 349 | + } |
| 350 | + ] |
| 351 | +} |
| 352 | +{ |
| 353 | + "source": "spine1", |
| 354 | + "subscription-name": "system-info", |
| 355 | + "timestamp": 1761025185169768456, |
| 356 | + "time": "2025-10-21T11:09:45.169768456+05:30", |
| 357 | + "updates": [ |
| 358 | + { |
| 359 | + "Path": "system/state/hostname", |
| 360 | + "values": { |
| 361 | + "system/state/hostname": "spine1" |
| 362 | + } |
| 363 | + } |
| 364 | + ] |
| 365 | +} |
| 366 | +{ |
| 367 | + "sync-response": true |
| 368 | +} |
| 369 | +{ |
| 370 | + "source": "spine1", |
| 371 | + "subscription-name": "mem-stats", |
| 372 | + "timestamp": 1761025184961897312, |
| 373 | + "time": "2025-10-21T11:09:44.961897312+05:30", |
| 374 | + "prefix": "eos_native:Kernel/proc/meminfo", |
| 375 | + "updates": [ |
| 376 | + { |
| 377 | + "Path": "bounce", |
| 378 | + "values": { |
| 379 | + "bounce": 0 |
| 380 | + } |
| 381 | + } |
| 382 | +``` |
| 383 | +
|
| 384 | +</p> |
| 385 | +</details> |
| 386 | +
|
| 387 | +Another collector which can be used is `gnmi_collector`, detailed example for the same is available |
| 388 | + in the [GitHub](https://github.com/aristanetworks/gnmi/tree/master/cmd/gnmi_collector) repo. |
| 389 | +
|
| 390 | +#### Troubleshooting |
| 391 | +
|
| 392 | +Use the following command to check the Octa/Openconfig agent logs: |
| 393 | +
|
| 394 | +```shell |
| 395 | +show agent Octa logs <<-- use this command if Octa is enabled |
| 396 | +show agent OpenConfig logs |
| 397 | +``` |
0 commit comments