{"id":18722394,"url":"https://github.com/nleiva/xrgrpc","last_synced_at":"2025-04-12T14:43:00.401Z","repository":{"id":22990883,"uuid":"96553000","full_name":"nleiva/xrgrpc","owner":"nleiva","description":"gRPC library for Cisco IOS XR","archived":false,"fork":false,"pushed_at":"2023-05-15T20:01:03.000Z","size":45648,"stargazers_count":57,"open_issues_count":1,"forks_count":15,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-23T07:33:28.775Z","etag":null,"topics":["cisco","cisco-ios-xr","golang","gpb","grpc","grpc-library","ios-xr","ios-xr-grpc","json","network-automation","network-programming","openconfig","protobuf","telemetry","telemetry-stream","yang"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nleiva.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-07T15:41:56.000Z","updated_at":"2024-06-12T16:01:07.000Z","dependencies_parsed_at":"2024-06-19T00:12:55.915Z","dependency_job_id":"200c57ec-b082-43bb-974d-5303ff865fbd","html_url":"https://github.com/nleiva/xrgrpc","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fxrgrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fxrgrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fxrgrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fxrgrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nleiva","download_url":"https://codeload.github.com/nleiva/xrgrpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248583542,"owners_count":21128618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cisco","cisco-ios-xr","golang","gpb","grpc","grpc-library","ios-xr","ios-xr-grpc","json","network-automation","network-programming","openconfig","protobuf","telemetry","telemetry-stream","yang"],"created_at":"2024-11-07T13:41:15.944Z","updated_at":"2025-04-12T14:43:00.380Z","avatar_url":"https://github.com/nleiva.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gRPC library for Cisco IOS XR\n\n[![GoDoc](https://godoc.org/github.com/nleiva/xrgrpc?status.svg)](https://godoc.org/github.com/nleiva/xrgrpc) \n[![Test](https://github.com/nleiva/xrgrpc/actions/workflows/test.yml/badge.svg)](https://github.com/nleiva/xrgrpc/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/nleiva/xrgrpc/branch/master/graph/badge.svg)](https://codecov.io/gh/nleiva/xrgrpc) \n[![Go Report Card](https://goreportcard.com/badge/github.com/nleiva/xrgrpc)](https://goreportcard.com/report/github.com/nleiva/xrgrpc)\n[![published](https://static.production.devnetcloud.com/codeexchange/assets/images/devnet-published.svg)](https://developer.cisco.com/codeexchange/github/repo/nleiva/xrgrpc)\n\nMinimalistic library to interact with IOS XR devices using the gRPC framework. Look at the [IOS XR proto file](proto/ems_grpc.proto) for the description of the service interface and the structure of the payload messages. gRPC uses protocol buffers as the Interface Definition Language (IDL).\n\n**Tutorials**: \n\n- [Programming IOS-XR with gRPC and Go](https://xrdocs.github.io/programmability/tutorials/2017-08-04-programming-ios-xr-with-grpc-and-go/).\n- [Validate the intent of network config changes](https://xrdocs.github.io/programmability/tutorials/2017-08-14-validate-the-intent-of-network-config-changes/).\n\n**Other Examples**:\n\n- [A collection of OpenConfig and Cisco IOS XR examples](https://github.com/nleiva/xroc).\n- [Parsing Telemetry data from IOS XR YANG models](https://github.com/nleiva/nettable).\n\nThe end goal is to enable use-cases where multiple interactions with devices are required. gRPC arises as a strong option to single interface network elements to retrieve info from the devices, apply configurations to it, generate telemetry streams from them, programming the RIB/FIB and so on. The following is a very simple config-validate example:\n\n![oc-config-validate](https://github.com/nleiva/xrgrpc/blob/gh-pages/static/images/oc-config-validateH.gif)\n\n## Table of Contents\n\n- [gRPC library for Cisco IOS XR](#grpc-library-for-cisco-ios-xr)\n  - [Table of Contents](#table-of-contents)\n  - [Prerequisite Tools](#prerequisite-tools)\n  - [Usage](#usage)\n    - [Get Config (example/getconfig)](#get-config-examplegetconfig)\n    - [Get Operation](#get-operation)\n    - [Show Commands](#show-commands)\n      - [Clear text](#clear-text)\n      - [JSON](#json)\n    - [Configuring the router](#configuring-the-router)\n      - [CLI config (Merge)](#cli-config-merge)\n      - [JSON (Merge)](#json-merge)\n      - [JSON (Replace)](#json-replace)\n      - [Using a YANG config Template (Merge)](#using-a-yang-config-template-merge)\n    - [Removing router config](#removing-router-config)\n      - [JSON](#json-1)\n    - [CLI config multiple routers simultaneously (Merge)](#cli-config-multiple-routers-simultaneously-merge)\n    - [Telemetry](#telemetry)\n      - [JSON (GPBKV)](#json-gpbkv)\n      - [JSON (GPBKV): Exploring the fields](#json-gpbkv-exploring-the-fields)\n      - [JSON (GPBKV): OpenConfig](#json-gpbkv-openconfig)\n      - [GPB (Protobuf)](#gpb-protobuf)\n    - [Config and Validate](#config-and-validate)\n    - [Actions](#actions)\n      - [Ping](#ping)\n      - [Traceroute](#traceroute)\n      - [Log Generation](#log-generation)\n      - [Crypto Key Generation](#crypto-key-generation)\n    - [Bypass the config file](#bypass-the-config-file)\n  - [XR gRPC Config](#xr-grpc-config)\n    - [Port range](#port-range)\n  - [Certificate file](#certificate-file)\n    - [Self-signed certificate for package testing](#self-signed-certificate-for-package-testing)\n  - [Generating Go binding from protobuf files](#generating-go-binding-from-protobuf-files)\n  - [Running the examples](#running-the-examples)\n    - [Links](#links)\n\n## Prerequisite Tools\n\n* [Go (at least Go 1.19)](https://golang.org/dl/)\n\n## Usage\n\nCLI examples to use the library are provided in the [example](example/) folder. The CLI specified in the examples is not definitive and might change as we go.\n\n### Get Config (example/getconfig)\n\nRetrieves the config from one target device described in [config.json](example/input/config.json), for the YANG paths specified in [yangpaths.json](example/input/yangpaths.json). If you want to see it using [OpenConfig models](https://github.com/openconfig/public/tree/master/release/models), you can issue `./getconfig -ypath \"../input/yangocpaths.json\"` instead.\n\n- `example/getconfig`\n\n```console\nexample/getconfig$  go run main.go \n\nconfig from sandbox-iosxr-1.cisco.com:57777\n {\n \"data\": {\n  \"openconfig-interfaces:interfaces\": {\n   \"interface\": [\n    {\n     \"name\": \"BVI511\",\n     \"config\": {\n      \"name\": \"BVI511\",\n      \"type\": \"iana-if-type:propVirtual\"\n     },\n     \"subinterfaces\": {\n      \"subinterface\": [\n       {\n        \"index\": 0,\n        \"openconfig-if-ip:ipv4\": {\n         \"addresses\": {\n          \"address\": [\n           {\n            \"ip\": \"10.200.188.33\",\n            \"config\": {\n             \"ip\": \"10.200.188.33\",\n             \"prefix-length\": 24\n            }\n           }\n          ]\n         }\n        }\n       }\n      ]\n     }\n    },\n    {\n\n...\n\n2022/05/11 16:55:36 This process took 715.136564ms\n```\n\n### Get Operation\n\nProvides the output of IOS XR operations data (eg. rib or route table) from YANG/JSON formatted request, from the list in [config.json](example/input/config.json). It reads the target from [getoper.json](example/input/getoper.json).\n\n-  example/getoper\n``` console\n$ ./getoper\n{\n \"Cisco-IOS-XR-ip-rib-ipv4-oper:rib\": {\n...\u003csnip\u003e...\n  \"vrfs\": {\n   \"vrf\": [\n    {\n     \"vrf-name\": \"default\",\n     \"afs\": {\n      \"af\": [\n       {\n        \"af-name\": \"IPv4\",\n        \"safs\": {\n         \"saf\": [\n          {\n           \"saf-name\": \"Unicast\",\n           \"ip-rib-route-table-names\": {\n            \"ip-rib-route-table-name\": [\n             {\n              \"route-table-name\": \"default\",\n              \"routes\": {\n               \"route\": [\n                {\n                 \"address\": \"1.1.1.100\",\n                 \"prefix-length\": 32,\n                 \"prefix\": \"1.1.1.100\",\n                 \"prefix-length-xr\": 32,\n                 \"route-version\": 2,\n                 \"protocol-id\": 1,\n                 \"protocol-name\": \"local\",\n...\u003csnip\u003e...\n                }\n\n```\n### Show Commands\n\nProvides the output of IOS XR cli commands for one router defined in [config.json](example/input/config.json). Two output format options are available; Unstructured text and JSON encoded:\n\n#### Clear text\n\n- example/showcmd\n\n```console\n$ ./showcmd -cli \"show isis database\" -enc text\n\nOutput from [2001:420:2cff:1204::5502:1]:57344\n \n----------------------------- show isis database ------------------------------\n\nIS-IS BB2 (Level-2) Link State Database\nLSPID                 LSP Seq Num  LSP Checksum  LSP Holdtime  ATT/P/OL\nmrstn-5502-1.cisco.com.00-00* 0x0000000c   0x1558        3066            0/0/0\nmrstn-5502-2.cisco.com.00-00  0x00000012   0x6e0c        3066            0/0/0\nmrstn-5501-1.cisco.com.00-00  0x0000000c   0x65d5        1150            0/0/0\n\n Total Level-2 LSP count: 3     Local Level-2 LSP count: 1\n\n\n2017/07/21 15:37:00 This process took 2.480039252s\n```\n\n#### JSON\n\n- example/showcmd\n\n```console\n$ ./showcmd -cli \"show isis database\" -enc json\n\nConfig from [2001:420:2cff:1204::5502:1]:57344\n [{\n \"Cisco-IOS-XR-clns-isis-oper:isis\": {\n\u003csnip\u003e\n       {\n        \"system-id\": \"0151.0250.0002\",\n        \"local-is-flag\": false,\n        \"host-levels\": \"isis-levels-2\",\n        \"host-name\": \"mrstn-5502-2.cisco.com\"\n       },\n       {\n        \"system-id\": \"0151.0250.0003\",\n        \"local-is-flag\": false,\n        \"host-levels\": \"isis-levels-2\",\n        \"host-name\": \"mrstn-5501-1.cisco.com\"\n       },\n       {\n        \"system-id\": \"0151.0250.0001\",\n        \"local-is-flag\": true,\n        \"host-levels\": \"isis-levels-2\",\n        \"host-name\": \"mrstn-5502-1.cisco.com\"\n...\n2017/07/21 15:37:27 This process took 1.54038192s\n```\n\n### Configuring the router\n\n#### CLI config (Merge)\n\nApplies CLI config commands on the device/router from the list in [config.json](example/input/config.json).\n\n- example/setconfig\n\n```console\n$ ./setconfig -cli \"interface Lo11 ipv6 address 2001:db8::/128\"\n\nConfig applied to [2001:420:2cff:1204::5502:1]:57344\n\n2017/07/21 15:24:17 This process took 1.779449886s\n```\n\nYou can verify the config on the router:\n\n```\nRP/0/RP0/CPU0:mrstn-5502-1.cisco.com#show run interface lo11\nFri Jul 21 15:24:24.199 EDT\ninterface Loopback11\n ipv6 address 2001:db8::/128\n!\n```\n\n#### JSON (Merge)\n\nApplies a YANG/JSON formatted config to one device/router (merges with existing config) from the list in [config.json](example/input/config.json). It reads the target from [yangconfig.json](example/input/yangconfig.json). \n\n- example/mergeconfig\n\n```console\n$ ./mergeconfig \n\nConfig merged on [2001:420:2cff:1204::5502:1]:57344 -\u003e Request ID: 8162, Response ID: 8162\n\n2017/07/21 15:18:07 This process took 1.531427437s\n```\n\nYou can verify the config on the router:\n\n```\nRP/0/RP0/CPU0:mrstn-5502-1.cisco.com#show run interface lo201\nFri Jul 21 15:18:24.046 EDT\ninterface Loopback201\n description New Loopback 201\n ipv6 address 2001:db8:20::1/128\n!\n```\n\n#### JSON (Replace)\n\nApplies a YANG/JSON formatted config to one device/router (replaces the config for this section) from the list in [config.json](example/input/config.json). It learns the config to replace from [yangconfigrep.json](example/input/yangconfigrep.json). If we had merged instead, we would have ended up with two IPv6 addresses in this example.\n\n- example/replaceconfig\n\n```console\n$ ./replaceconfig \n\nConfig replaced on [2001:420:2cff:1204::5502:1]:57344 -\u003e Request ID: 4616, Response ID: 4616\n\n2017/07/21 15:21:27 This process took 1.623047025s\n```\n\nYou can verify the config on the router:\n\n```\nRP/0/RP0/CPU0:mrstn-5502-1.cisco.com#show run interface lo201\nFri Jul 21 15:21:48.053 EDT\ninterface Loopback201\n description New Loopback 221\n ipv6 address 2001:db8:22::2/128\n!\n```\n\n#### Using a YANG config Template (Merge)\n\nApplies a YANG/JSON formatted config to one device/router (merges with existing config) from the list in [config.json](example/input/config.json). It takes a template ([bgp.json](example/input/template/bgp.json)), based on the BGP YANG model [Cisco-IOS-XR-ipv4-bgp-cfg](https://github.com/YangModels/yang/blob/master/vendor/cisco/xr/622/Cisco-IOS-XR-ipv4-bgp-cfg.yang), in this case and the specific parameters from [bgp-parameters.json](example/input/template/bgp-parameters.json).\n\nSee below an extract from this [bgp.json](example/input/template/bgp.json) and notice NeighborAddress, PeerASN, Description and LocalAddress are variables to be defined.\n\n```shell\n\"neighbor\": [\n {\n  \"neighbor-address\": \"{{.NeighborAddress}}\",\n  \"remote-as\": {\n   \"as-xx\": {{.PeerASN.X}},\n   \"as-yy\": {{.PeerASN.Y}}\n  },\n  \"description\": \"{{.Description}}\",\n  \"update-source-interface\": \"{{.LocalAddress}}\",\n  \"neighbor-afs\": {\n   \"neighbor-af\": [\n\t{\n\t \"af-name\": \"ipv6-unicast\",\n\t \"activate\": [\n\t  null\n\t ]\n\t}\n   ]\n  }\n }\n] \n```\n\nNow we execute and inmediately request the updated BGP config from the device with a subsequent RPC call.\n\n- example/mergetemplate\n\n```console\n$ ./mergetemplate \n\nConfig merged on [2001:420:2cff:1204::5502:1]:57344 -\u003e Request ID: 1866, Response ID: 1866\n\n\nConfig from [2001:420:2cff:1204::5502:1]:57344\n {\n \"Cisco-IOS-XR-ipv4-bgp-cfg:bgp\": {\n  \"instance\": [\n\u003csnip\u003e\n         \"bgp-entity\": {\n          \"neighbors\": {\n           \"neighbor\": [\n            {\n             \"neighbor-address\": \"2001:db8:1::1\",\n             \"remote-as\": {\n              \"as-xx\": 0,\n              \"as-yy\": 65535\n             },\n             \"description\": \"Test\",\n             \"update-source-interface\": \"Loopback60\",\n             \"neighbor-afs\": {\n              \"neighbor-af\": [\n\u003csnip\u003e\n\n2017/08/07 18:52:57 This process took 907.395197ms\n```\n\nGo includes the [template](https://golang.org/pkg/html/template/) package in its standard library to generate data-driven textual outputs. \n- Give templates and YANG a try in [The Go Playground](https://play.golang.org/p/xRsTkVfCTG).\n\nWhile templates are cool, I'd recommend exploring one of these alternatives to handle YANG models programmatically.\n- [YDK](https://developer.cisco.com/site/ydk/) that takes YANG models as input and produces APIs that mirror the structure of the models.\n- [goyang](https://github.com/openconfig/goyang) which is a YANG parser and compiler to produce Go language objects.\n\n### Removing router config\n\n#### JSON\n\nRemoves YANG/JSON formatted config on one device/router from [config.json](example/input/config.json). It reads the config to delete from [yangdelconfig.json](example/input/yangdelconfig.json). The following example deletes both interfaces configured in the Merge example. See [yangdelintadd.json](example/input/yangdelintadd.json) to delete just the IP address and [yangdelintdesc.json](example/input/yangdelintdesc.json) for only the description of the interface.\n\n- example/deleteconfig\n\n```console\n$ ./deleteconfig \n\nConfig Deleted on [2001:420:2cff:1204::5502:1]:57344 -\u003e Request ID: 2856, Response ID: 2856\n\n2017/07/21 15:06:46 This process took 730.329288ms\n```\n\nOn the router:\n\n```\nRP/0/RP0/CPU0:mrstn-5502-1.cisco.com#show configuration commit changes 1000000039\nMon Jul 17 15:54:59.221 EDT\nBuilding configuration...\n!! IOS XR Configuration version = 6.2.2.22I\nno interface Loopback201\nno interface Loopback301\nend\n```\n\n### CLI config multiple routers simultaneously (Merge)\n\nApplies CLI config commands to the list of routers specified on [config.json](example/input/config.json). Notice that even though we added two devices, the execution time did NOT increase. This is possible because of the use of [Golang Concurrency](https://blog.golang.org/pipelines) primitives.\n\n- example/setconfiglist\n\n```console\n$ ./setconfiglist -cli \"interface Lo33 ipv6 address 2001:db8:33::1/128\"\n\nConfig applied to [2001:420:2cff:1204::5502:2]:57344\n\n\n\nConfig applied to [2001:420:2cff:1204::5501:1]:57344\n\n\n\nConfig applied to [2001:420:2cff:1204::5502:1]:57344\n\n\n2017/07/21 15:32:11 This process took 1.773893901s\n```\n\nYou can verify the config on the routers:\n\n```\nRP/0/RP0/CPU0:mrstn-5501-1.cisco.com#sh run int Lo33\nFri Jul 21 15:32:35.468 EDT\ninterface Loopback33\n ipv6 address 2001:db8:33::1/128\n!\n```\n\n```\nRP/0/RP0/CPU0:mrstn-5502-1.cisco.com#sh run int Lo33\nFri Jul 21 15:33:07.281 EDT\ninterface Loopback33\n ipv6 address 2001:db8:33::1/128\n!\n```\n\n```\nRP/0/RP0/CPU0:mrstn-5502-2.cisco.com#sh run int Lo33\nFri Jul 21 15:33:14.504 EDT\ninterface Loopback33\n ipv6 address 2001:db8:33::1/128\n!\n```\n\n\n### Telemetry\n\n#### JSON (GPBKV)\n\nSubscribe to a Telemetry stream. The Telemetry message is defined in [telemetry.proto](proto/telemetry/telemetry.proto). The payload is JSON encoded (self-describing GPB).\n\n- example/telemetry\n\n```console\n$ ./telemetry -subs \"LLDP\"\nTime 1500666991103, Path: Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\n{\n  \"NodeId\": {\n    \"NodeIdStr\": \"mrstn-5502-1.cisco.com\"\n  },\n  \"Subscription\": {\n    \"SubscriptionIdStr\": \"LLDP\"\n  },\n  \"encoding_path\": \"Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\",\n  \"collection_id\": 1,\n  \"collection_start_time\": 1500666991103,\n  \"msg_timestamp\": 1500666991103,\n  \"data_gpbkv\": [\n    {\n      \"timestamp\": 1500666991108,\n      \"ValueByType\": null,\n      \"fields\": [\n...\n```\n\nThe Subscription ID has to exist on the device \u003csup\u003e[1](#myfootnote1)\u003c/sup\u003e.\n\n```\ntelemetry model-driven\n sensor-group LLDPNeighbor\n  sensor-path Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\n !\n subscription LLDP\n  sensor-group-id LLDPNeighbor sample-interval 15000\n !\n!\n```\n\n#### JSON (GPBKV): Exploring the fields\n\nSame as the previous example using a Cisco native YANG model. However this time we explore the fields in order to produce a custom output.\n\n```go\nfunc exploreFields(f []*telemetry.TelemetryField, indent string) {\n\tfor _, field := range f {\n\t\tswitch field.GetFields() {\n\t\tcase nil:\n\t\t\tdecodeKV(field, indent)\n\t\tdefault:\n\t\t\texploreFields(field.GetFields(), indent+\" \")\n\t\t}\n\t}\n}\n```\n\nThe result looks like this:\n\n- example/telemetrykv\n\n```console\n$ ./telemetrykv\n******************************************************************************************\nTime 01:24:48PM, Path: Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\n******************************************************************************************\n  node-name: 0/RP0/CPU0\n  interface-name: HundredGigE0/0/0/1\n  device-id: mrstn-5502-1.cisco.com\n   receiving-interface-name: HundredGigE0/0/0/1\n   receiving-parent-interface-name: \u003cNo interface\u003e\n   device-id: mrstn-5502-1.cisco.com\n   chassis-id: 008a.9646.6cd8\n   port-id-detail: Hu0/0/0/1\n   header-version: 0\n   hold-time: 15\n   enabled-capabilities: R\n   platform:\n    port-description: TO calient_fiber_switch, port 001 in/out\n    system-name: mrstn-5502-1.cisco.com\n    system-description:  6.2.2.22I, NCS-5500\n\u003csnip\u003e    \n```\n\n#### JSON (GPBKV): OpenConfig\n\nSame example as before, just calling a subscription that uses an OpenConfig model instead. The result looks like this:\n\n- example/telemetrykv\n\n```console\n$ ./telemetrykv -subs \"BGP-OC\"\n******************************************************************************************\nTime 01:08:03PM, Path: openconfig-bgp:bgp/neighbors/neighbor/state\n******************************************************************************************\n  instance-name: default\n  neighbor-address: 2001:db8:cafe::2\n  speaker-id: 0\n  description: iBGP session\n  local-as: 64512\n  remote-as: 64512\n  has-internal-link: true\n  is-external-neighbor-not-directly-connected: false\n  messages-received: 16\n  messages-sent: 16\n  update-messages-in: 1\n  update-messages-out: 1\n  messages-queued-in: 0\n  messages-queued-out: 0\n  connection-established-time: 822\n  connection-state: bgp-st-estab\n  previous-connection-state: 2\n  connection-admin-status: 0\n  open-check-error-code: none\n   afi: ipv6\n    value: 2001:db8:cafe::1\n  is-local-address-configured: false\n\u003csnip\u003e    \n```\n\nThe Subscription ID has to exist on the device \u003csup\u003e[1](#myfootnote1)\u003c/sup\u003e.\n\n```\ntelemetry model-driven\n sensor-group BGPNeighbor-OC\n  sensor-path openconfig-bgp:bgp/neighbors/neighbor/state\n !\n subscription BGP-OC\n  sensor-group-id BGPNeighbor-OC sample-interval 10000\n !\n!\n```\n\n#### GPB (Protobuf)\n\nAgain, we subscribe to a Telemetry stream but we request the content is encoded with [protobuf](https://developers.google.com/protocol-buffers/). To decode the message we need to look at the \"LLDP neighbor details\" definition in [lldp_neighbor.proto](proto/telemetry/lldp/lldp_neighbor.proto). We parse the message and modify the output to illustrate how to access to each field on it.\n\n- example/telemetrygpb\n\n```console\n$ ./telemetrygpb -subs \"LLDP\"\nTime 1500667512299, Path: Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\n{\n  \"node_name\": \"0/RP0/CPU0\",\n  \"interface_name\": \"HundredGigE0/0/0/22\",\n  \"device_id\": \"mrstn-5502-2.cisco.com\"\n}\nType:  6.2.2.22I, NCS-5500, Address value:\"2001:558:2::2\"  \n\n{\n  \"node_name\": \"0/RP0/CPU0\",\n  \"interface_name\": \"HundredGigE0/0/0/21\",\n  \"device_id\": \"mrstn-5502-2.cisco.com\"\n}\nType:  6.2.2.22I, NCS-5500, Address value:\"2001:558:2::2\"  \n\n{\n  \"node_name\": \"0/RP0/CPU0\",\n  \"interface_name\": \"HundredGigE0/0/0/1\",\n  \"device_id\": \"mrstn-5502-2.cisco.com\"\n}\nType:  6.2.2.22I, NCS-5500, Address value:\"2001:f00:bb::2\"\n...\n```\n\nTo print out the entire message, look at the example in [telemetrygpbfull](example/telemetrygpbfull/main.go). To decode the message we need to look at the \"LLDP neighbor details\" definition in [lldp_neighbor.proto](proto/telemetry/lldp65x/lldp_neighbor.proto).\n\n- example/telemetrygpbfull\n\n```console\n$ go run main.go\nTime 1557511431921, Path: Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\nDecoded Keys:\n{\n  \"node_name\": \"0/0/CPU0\",\n  \"interface_name\": \"HundredGigE0/0/0/0\",\n  \"device_id\": \"mrstn-5502-1.cisco.com\"\n}\nDecoded JSON Neighbors:\n{\n  \"lldp_neighbor\": [\n    {\n      \"receiving_interface_name\": \"HundredGigE0/0/0/0\",\n      \"device_id\": \"mrstn-5502-1.cisco.com\",\n      \"chassis_id\": \"008a.9646.6cdf\",\n      \"port_id_detail\": \"HundredGigE0/0/0/0\",\n      \"hold_time\": 15,\n      \"enabled_capabilities\": \"R\",\n      \"detail\": {\n        \"peer_mac_address\": \"00:8a:96:46:6c:00\",\n        \"port_description\": \"TRANSPORT: mrstn-5502-2.cisco.com\",\n        \"system_name\": \"mrstn-5502-1.cisco.com\",\n        \"system_description\": \" 6.5.3.02I, NCS-5500\",\n        \"time_remaining\": 11,\n        \"system_capabilities\": \"R\",\n        \"enabled_capabilities\": \"R\",\n        \"network_addresses\": {\n        ...\n```\n\nThe Subscription ID has to exist on the device \u003csup\u003e[1](#myfootnote1)\u003c/sup\u003e.\n\n```\ntelemetry model-driven\n sensor-group LLDPNeighbor\n  sensor-path Cisco-IOS-XR-ethernet-lldp-oper:lldp/nodes/node/neighbors/details/detail\n !\n subscription LLDP\n  sensor-group-id LLDPNeighbor sample-interval 15000\n !\n!\n```\n\u003ca name=\"myfootnote1\"\u003e[1]\u003c/a\u003e: [gNMI](https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi.proto) defines a variant where you do not need this config.\n\n### Config and Validate\n\nIn order to validate the intended state of the network after a config change, we need to need to look at the associated telemetry data. In this example we will configure a BGP neighbor using a BGP config [template](https://golang.org/pkg/html/template/) based on the [OpenConfig BGP YANG model](https://github.com/openconfig/public/tree/master/release/models/bgp). See below an extract of [oc-bgp.json](example/input/template/oc-bgp.json).\n\n```shell\n{ \"openconfig-bgp:bgp\": {\n   \"global\": {\n    \"config\": {\n     \"as\": {{.LocalAs}}\n    }\n   },\n   \"neighbors\": {\n    \"neighbor\": [\n     {\n      \"neighbor-address\": \"{{.NeighborAddress}}\",\n      \"config\": {\n       \"neighbor-address\": \"{{.NeighborAddress}}\",\n       \"peer-as\": {{.PeerAs}},\n       \"description\": \"{{.Description}}\"\n      }\n\u003csnip\u003e\n```\n\nThe example will run a config checklist, composed of three items as a result of independent RPC calls.\n\n1. We obtain a gRPC confirmation that config was received by the target.\n2. We make a gRPC request to get the running configuration on the target to validate the change submitted was actually applied.\n3. We subscribe to a BGP Neighbor State Telemetry stream to track the status changes.\n\nThe output of the example is very basic, but illustrates all these points. Notice we receive BGP status every 5 seconds and the neighbor goes from bgp-st-idle to bgp-st-estab.\n\n- example/configvalidate\n\n```console\n$ ./configvalidate \n******************************************************************************************\n\nConfig merged on [2001:420:2cff:1204::5502:1]:57344 -\u003e Request ID: 3018, Response ID: 3018\n\n******************************************************************************************\n\nBGP Config from [2001:420:2cff:1204::5502:1]:57344\n\n\n{\n \"openconfig-bgp:bgp\": {\n  \"global\": {\n   \"config\": {\n    \"as\": 64512,\n    \"router-id\": \"162.151.250.1\"\n   },\n   \"afi-safis\": {\n    \"afi-safi\": [\n     {\n      \"afi-safi-name\": \"openconfig-bgp-types:ipv6-unicast\",\n      \"config\": {\n       \"afi-safi-name\": \"openconfig-bgp-types:ipv6-unicast\",\n       \"enabled\": true\n      }\n     }\n    ]\n   }\n  },\n  \"neighbors\": {\n   \"neighbor\": [\n    {\n     \"neighbor-address\": \"2001:db8:cafe::2\",\n     \"config\": {\n      \"neighbor-address\": \"2001:db8:cafe::2\",\n      \"peer-as\": 64512,\n      \"description\": \"iBGP session\"\n     },\n     \"afi-safis\": {\n      \"afi-safi\": [\n       {\n        \"afi-safi-name\": \"openconfig-bgp-types:ipv6-unicast\",\n        \"config\": {\n         \"afi-safi-name\": \"openconfig-bgp-types:ipv6-unicast\",\n         \"enabled\": true\n        }\n       }\n      ]\n     }\n    }\n   ]\n  }\n }\n}\n\n******************************************************************************************\n\nTelemetry from [2001:420:2cff:1204::5502:1]:57344\n\n------------------------------------- Time 02:39:06AM -------------------------------------\nBGP Neighbor; IP: 2001:db8:cafe::2, ASN: 64512, State bgp-st-idle \n\n------------------------------------- Time 02:39:11AM -------------------------------------\nBGP Neighbor; IP: 2001:db8:cafe::2, ASN: 64512, State bgp-st-idle \n\n------------------------------------- Time 02:39:16AM -------------------------------------\nBGP Neighbor; IP: 2001:db8:cafe::2, ASN: 64512, State bgp-st-idle \n\n------------------------------------- Time 02:39:21AM -------------------------------------\nBGP Neighbor; IP: 2001:db8:cafe::2, ASN: 64512, State bgp-st-estab\n```\n\nThe telemetry subscription config is:\n\n```\ntelemetry model-driven\n sensor-group BGPNeighbor\n  sensor-path Cisco-IOS-XR-ipv4-bgp-oper:bgp/instances/instance/instance-active/default-vrf/afs/af/neighbor-af-table/neighbor\n !\n subscription BGP\n  sensor-group-id BGPNeighbor sample-interval 2000\n !\n!\n```\n\n### Actions\n\nThere are multiple actions than can be triggered via gRPC on IOS XR devices running 6.3.1 or later. Below the YANG models supported to the date and some examples using this library.\n\n```console\n$ cd ~/yang/vendor/cisco/xr/641\n$ ls | grep act.yang\nCisco-IOS-XR-cfgmgr-rollback-act.yang\nCisco-IOS-XR-crypto-act.yang\nCisco-IOS-XR-hwmod-mpa-reload-act.yang\nCisco-IOS-XR-infra-statsd-act.yang\nCisco-IOS-XR-ipv4-bgp-act.yang\nCisco-IOS-XR-ipv4-ospf-act.yang\nCisco-IOS-XR-ipv4-ping-act.yang\nCisco-IOS-XR-ipv4-traceroute-act.yang\n...\n```\n\n#### Ping\n\n- IPv6 Ping (`example/action` with [ping6.json](example/input/action/ping6.json))\n\n```console\n$ ./action -act \"../input/action/ping6.json\"\n\noutput from [2001:420:2cff:1204::7816:1]:57344\n {\n \"Cisco-IOS-XR-ping-act:output\": {...}\n}\n\n2018/05/29 15:08:34 This process took 800.050845ms\n```\n\nThe extended output looks like this:\n\n```json\n {\n \"Cisco-IOS-XR-ping-act:output\": {\n  \"ping-response\": {\n   \"ipv6\": {\n    \"destination\": \"2001:420:2cff:1204::1\",\n    \"repeat-count\": \"2\",\n    \"data-size\": \"1350\",\n    \"timeout\": \"1\",\n    \"pattern\": \"abcd\",\n    \"rotate-pattern\": false,\n    \"replies\": {\n     \"reply\": [\n      {\n       \"reply-index\": \"1\",\n       \"result\": \"!\"\n      },\n      {\n       \"reply-index\": \"2\",\n       \"result\": \"!\"\n      }\n     ]\n    },\n    \"hits\": \"2\",\n    \"total\": \"2\",\n    \"success-rate\": \"100\",\n    \"rtt-min\": \"2\",\n    \"rtt-avg\": \"2\",\n    \"rtt-max\": \"3\"\n   }\n  }\n }\n}\n```\n\n![ipv6-ping](https://github.com/nleiva/xrgrpc/blob/gh-pages/static/images/ipv6_ping.svg)\n\n#### Traceroute\n\n- Dual-Stack traceroute (`example/action` with [traceroute.json](example/input/action/traceroute.json)). Target is an FQDN ([www.cisco.com](www.cisco.com)).\n\n```console\n ./action -act \"../input/action/traceroute.json\"\n\noutput from [2001:420:2cff:1204::7816:1]:57344\n {\n \"Cisco-IOS-XR-traceroute-act:output\": {\n  \"traceroute-response\": {\n   \"ipv6\": {\n    \"destination\": \"2001:420:1101:1::a\",\n    \"hops\": {\n     \"hop\": [\n      {\n       \"hop-index\": 1,\n       \"hop-address\": \"2001:420:2cff:1204::1\",\n       \"hop-hostname\": \"mrstn-vlan44.cisco.com\",\n       \"probes\": {\n        \"probe\": [\n         {\n          \"probe-index\": 0,\n          \"delta-time\": 1\n         },\n         {\n          \"probe-index\": 1,\n          \"delta-time\": 1\n         },\n         {\n          \"probe-index\": 2,\n          \"delta-time\": 1\n         }\n        ]\n       }\n      },\n...\n\n2018/05/29 15:12:50 This process took 15.241451154s\n```\n\n#### Log Generation\n\n- Log event generation (`example/action` with [log.json](example/input/action/log.json)).\n\n```console\n$ ./action -act \"../input/action/log.json\"\n\noutput from [2001:420:2cff:1204::7816:1]:57344\n\n2018/05/29 15:23:59 This process took 42.585985ms\n```\n\nOn the router\n\n```console\n#\nRP/0/RP0/CPU0:May 29 19:23:59.297 UTC: emsd[1096]: %OS-SYSLOG-1-LOG_ALERT : gRPC Generated: Device will be under maintenance for 2 hrs for planned activities\n```\n\n![log](https://github.com/nleiva/xrgrpc/blob/gh-pages/static/images/log.svg)\n\n#### Crypto Key Generation\n\n- SSH Crypto key generation (`example/action` with [crypto.json](example/input/action/crypto.json)).\n\n```console\n$ ./action -act \"../input/action/crypto.json\"\n\noutput from [2001:420:2cff:1204::7816:1]:57344\n\n2018/05/29 15:26:06 This process took 2.721703931s\n```\n\n```console\n#\nRP/0/RP0/CPU0:May 29 19:26:04.249 UTC: cepki[420]: %SECURITY-CEPKI-6-KEY_INFO : crypto key RSA zeroized, label:test\nRP/0/RP0/CPU0:May 29 19:26:06.907 UTC: cepki[420]: %SECURITY-CEPKI-6-KEY_INFO : crypto key RSA generated, label:test, modBits:2048\nRP/0/RP0/CPU0:May 29 19:26:06.917 UTC: cepki[420]: %SECURITY-CEPKI-6-INFO : key database updated\n```\n\n![crypto](https://github.com/nleiva/xrgrpc/blob/gh-pages/static/images/crypto.svg)\n\n### Bypass the config file\n\nYou can manually define the target without the config file [config.json](example/input/config.json), by calling the functional options \"WithValue\". See the snippet below from [definetarget](example/definetarget/main.go).\n\n```go\n// Manually specify target parameters.\nrouter, err := xr.BuildRouter(\n  xr.WithUsername(\"admin\"),\n  xr.WithPassword(\"C1sco12345\"),\n  xr.WithHost(\"sandbox-iosxr-1.cisco.com:57777\"),\n  xr.WithTimeout(45),\n)\n```\n\n## XR gRPC Config\n\nThe following is the configuration required on the IOS XR device in order to enable gRPC dial-in with TLS support.\n\n```\ntpa\n vrf default\n  address-family ipv4\n   default-route mgmt\n  !\n !\n!\ngrpc\n port 57777\n address-family ipv4\n!\n```\n\n### Port range\n\nWhile you can select any not-used port on the device, it's recommended to choose one from the 57344-57999 range.\n\n```\nmrstn-5502-1 emsd: [1058]: %MGBL-EMS-4-EMSD_PORT_RANGE : The configured port 56500 is outside of the range of [57344, 57999]. It will consume an additional LPTS entry.\n```\n\n## Certificate file\n\nUpdate 6/24/2019: You no longer need to download the certicate file manually. If you don't specify a file, it will be downloaded automatically as in the [getconfig](example/getconfig/main.go) example.\n\n### Self-signed certificate for package testing\n\nThis needs to be renewed once a year.\n\n```console\n$ openssl req -new -x509 -nodes -subj '/C=US/CN=localhost' \\\n              -addext \"subjectAltName = DNS:localhost\" \\\n              -newkey rsa:4096 -keyout test/key.pem -out \\\n              test/cert.pem -days 365\n```\n\n## Generating Go binding from protobuf files\n\nThe Go generated code in [ems_grpc.pb.go](proto/ems/ems_grpc.pb.go) is the result of the following:\n\n- `proto/ems`\n\n```console\n$ protoc --go_out=. --go_opt=paths=source_relative \\\n    --go-grpc_out=. --go-grpc_opt=paths=source_relative \\\n    --go_opt=Mproto/ems/ems_grpc.proto=proto/ems \\\n    --go-grpc_opt=Mproto/ems/ems_grpc.proto=proto/ems \\\n    proto/ems/ems_grpc.proto\n```\n\n- The Go generated code in [bgp_nbr_bag.pb.go](proto/telemetry/bgp/bgp_nbr_bag.pb.go) is the result of the following:\n\n```console\n$ protoc --go_out=. \\\n    --go_opt=Mproto/telemetry/bgp/bgp_nbr_bag.proto=proto/telemetry/bgp \\\n    proto/telemetry/bgp/bgp_nbr_bag.proto\n```\n\n- The Go generated code in [lldp_neighbor.pb.go](proto/telemetry/lldp/lldp_neighbor.pb.go) is the result of the following:\n\n```console\n$ protoc --go_out=. \\\n    --go_opt=Mproto/telemetry/lldp/lldp_neighbor.proto=proto/telemetry/lldp \\\n    proto/telemetry/lldp/lldp_neighbor.proto\n```\n\n## Running the examples\n\nAfter cloning the repo, go the a folder example and execute `go run main.go`. For example:\n\n```console\n$ cd example/configvalidate\n$ go run main.go \n```\n\n### Links\n\n- [XR YANG models](https://github.com/YangModels/yang/tree/main/vendor/cisco/xr)\n- [XR Proto files](https://github.com/ios-xr/model-driven-telemetry/tree/master/protos)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnleiva%2Fxrgrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnleiva%2Fxrgrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnleiva%2Fxrgrpc/lists"}