项目作者: ksator

项目描述 :
Network automation and telemetry demo with EOS devices
高级语言: Jinja
项目地址: git://github.com/ksator/automation_and_telemetry_demo.git
创建时间: 2021-01-30T15:44:07Z
项目社区:https://github.com/ksator/automation_and_telemetry_demo

开源协议:Apache License 2.0

下载


This repository has Network automation demo and telemetry demo with EOS devices

Table of content

Set up an automation VM

We are using this Ubuntu VM for this workshop:

  1. $ lsb_release -a
  2. No LSB modules are available.
  3. Distributor ID: Ubuntu
  4. Description: Ubuntu 18.04.1 LTS
  5. Release: 18.04
  6. Codename: bionic

Run these commands to update the VM and install tools

  1. sudo apt-get update
  2. sudo apt-get -y upgrade
  3. sudo apt-get install tree snmp python3-pip build-essential libssl-dev libffi-dev python3-dev -y
  4. sudo apt install jq
  5. pip3 install napalm netmiko jsonrpclib-pelix pyang pyangbind ansible==2.9.15 influxdb pygnmi

Check

  1. pip3 list
  1. $ python3 -V
  2. Python 3.6.9
  1. ansible --version
  2. pyang --version

Then install also:

Check

  1. docker version
  2. docker-compose version
  3. gnmic version

Clone this repository

Clone this repository

And then move to the local directory

  1. cd automation_and_telemetry_demo

Configure EOS devices

Configure all EOS devices for gNMI and SNMP and eAPI

  1. snmp-server community public ro
  2. snmp-server vrf MGMT
  1. username arista secret 0 arista
  1. management api gnmi
  2. transport grpc def
  3. vrf MGMT
  4. provider eos-native
  1. management api http-commands
  2. protocol http
  3. no shutdown

and verify

  1. DC1-LEAF1A#sho management http-server
  2. SSL Profile: none
  3. FIPS Mode: No
  4. QoS DSCP: 0
  5. Log Level: none
  6. CSP Frame Ancestor: None
  7. TLS Protocols: 1.0 1.1 1.2
  8. VRF Server Status Enabled Services
  9. ---------- --------------------- ----------------
  10. MGMT HTTPS: port 443 http-commands
  1. DC1-LEAF1A#sho management api gnmi
  2. Octa: enabled
  3. Enabled: Yes
  4. Server: running on port 6030, in MGMT VRF
  5. SSL Profile: none
  6. QoS DSCP: none

no need to enable RESTCONF and NETCONF for this demo

  1. DC1-LEAF1A#sho management api restconf
  2. Enabled: No
  3. Server: Not yet running
  4. SSL Profile: none
  5. QoS DSCP: none
  1. DC1-LEAF1A#sho management api netconf
  2. Enabled: No
  3. Server: Not yet running
  4. DC1-LEAF1A#

Netmiko

Netmiko is a python library to simplify SSH connections to network devices.

So we can use it even when API is disabled on EOS devices (default). As example we can use it to enable API on EOS devices.

From the root of this repository, move to the netmiko directory

  1. cd netmiko
  1. python3 collect_show_commands.py
  2. more "show version.txt"
  3. more "show ip interface brief.txt"
  1. more config.txt
  2. python3 configure_from_file.py

eAPI (EOS API)

Once the API is enabled, the switch accepts HTTP(S) requests containing a list of EOS commands, and responds with machine-readable output serialized in JSON (served over HTTP or HTTPS).

From the root of this repository, move to the eAPI directory

  1. cd eapi
  1. python3 test1.py
  2. python3 test2.py

Ansible

We will use the ansible module eos_command and eAPI to run show commands on EOS devices.

From the root of the repository, move to the Ansible directory

  1. cd ansible

Update the inventory

Update the inventory.yml file

Update the variables group_vars and host_vars directories

Basic demo

  1. ansible-playbook playbooks/print_version_and_models.yml

Collect show commands from the devices

Update the list of show commands you want to collect (this is an ansible variable currently defined in the group_vars directory) and execute this playbook:

  1. ansible-playbook playbooks/snapshots.yml

The output of the show commands is saved in the directory snaphots

  1. tree snapshots

Test the devices and generate a report

To run all the tests (NTP, LLDP, interfaces state, temperature, …):

  1. ansible-playbook playbooks/tests.yml

This will generate this markdown report and this CSV report

  1. ls reports
  2. more reports/POC-state.md
  3. more reports/POC-state.csv

To run all only some tests, use ansible tags.
Examples:

  1. ansible-playbook playbooks/tests.yml --tags lldp
  2. ansible-playbook playbooks/tests.yml --tags "hardware, ntp, reload_cause, arbgp"

Pyang

About Pyang

pyang is a python program.
We can use it to:

  • Validate YANG modules against YANG RFCs
  • Convert YANG modules into equivalent YIN module (XML)
  • Generate a tree representation of YANG models for quick visualization
  1. pip3 freeze | grep pyang

Get YANG modules

We need YANG files.

Clone the openconfig repository

From the root of this repository:

  1. git clone https://github.com/openconfig/public.git
  1. ls public

Copy all the YANG files from OpenConfig to the yang_modules directory

  1. cp public/release/models/*.yang yang_modules/.
  2. cp -R public/release/models/*/*.yang yang_modules/.
  3. cp public/third_party/ietf/*.yang yang_modules/.

Move to the yang_modules directory

It has all the YANG files published on the OpenConfig repository

  1. cd yang_modules/
  2. ls

Validate YANG modules

  1. pyang openconfig-bgp.yang
  2. pyang openconfig-interfaces.yang

Convert a YANG module into an equivalent YIN module

A YANG module can be translated into an XML syntax called YIN. The translated module is called a YIN module. The YANG and YIN formats contain equivalent information using different notations: YIN is YANG in XML. A YANG module can be translated into YIN syntax without losing any information.

Example (openconfig-bgp.yin is the YIN equivalent of openconfig-bgp.yang)

  1. pyang openconfig-bgp.yang -f yin -o openconfig-bgp.yin
  2. ls *.yin

Generate a tree representation of YANG modules for quick visualization

  1. pyang openconfig-interfaces.yang -f tree
  2. pyang openconfig-interfaces.yang -f tree --tree-path=/interfaces/interface/state
  3. pyang openconfig-interfaces.yang -f tree --tree-depth=4
  1. pyang openconfig-bgp.yang -f tree --tree-path=/bgp/neighbors --tree-depth=4
  2. pyang openconfig-bgp.yang -f tree --tree-path=/bgp/neighbors/neighbor/config
  3. pyang openconfig-bgp.yang -f tree --tree-path=/bgp/neighbors/neighbor/state --tree-depth=5
  4. pyang openconfig-bgp.yang -f tree --tree-path=/bgp/neighbors/neighbor/afi-safis --tree-depth=6
  1. pyang openconfig-network-instance.yang -f tree --tree-depth=4
  2. pyang openconfig-network-instance.yang -f tree --tree-path=/network-instances/network-instance/protocols/protocol/bgp --tree-depth=6
  3. pyang openconfig-network-instance.yang -f tree --tree-path=/network-instances/network-instance/protocols/protocol/isis --tree-depth=6

PyangBind

About PyangBind

PyangBind is a pyang plugin.
It generates Python classes from a YANG module: It converts YANG module into a Python module, such that Python can be used to generate data which conforms with the data model defined in YANG.

  1. pip3 freeze | grep pyang

From the root of this repository:

  1. cd yang_modules/

Generate a Python module from a YANG module

  1. pyang --plugindir $HOME/.local/lib/python3.6/site-packages/pyangbind/plugin/ -f pybind -o oc_bgp.py openconfig-bgp.yang

The above command generated the python module oc_bgp.py from the openconfig-bgp.yang file

  1. ls oc_bgp.py

Use the new python module to generate OpenConfig configuration

The file pyangbind_demo.py uses the new python module oc_bgp.py to generate this OpenConfig configuration file

  1. more pyangbind_demo.py
  2. python3 pyangbind_demo.py
  1. more ../gnmi/test.json

This configuration will be loaded later on a switch using the gNMI Set RPC

gNMIc

We will use gNMIc (an open source gNMI client)

  1. gnmic version

From the root of this repository, move to the gNMI directory

  1. cd gnmi/

Lets use the following gNMI RPC: Capabilities, Get, Set, Subscribe.

gNMI Capabilities RPC

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure capabilities

gNMI Get RPC

Retrieve a snapshot for a path

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "/interfaces/interface[name=Ethernet2]/config/description"
  2. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "/interfaces/interface[name=Ethernet1]/config/enabled"
  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "/network-instances/network-instance[name=default]/protocols/protocol[name=BGP]/bgp"
  2. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path '/network-instances/network-instance[name=default]/protocols/protocol[name=BGP]/bgp/neighbors'
  3. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "network-instances/network-instance[name=default]/protocols/protocol[identifier=BGP]/bgp[afi-safi-name=IPV4_UNICAST]"
  4. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "network-instances/network-instance[name=default]/protocols/protocol[identifier=BGP][name=BGP]/bgp/neighbors/neighbor[neighbor-address=172.31.255.8]/afi-safis/afi-safi"

gNMI Set RPC

The Set RPC is used to modify states.

The SetRequest message uses the following fields:

  • “delete” field: A set of paths which are to be removed from the data tree
  • “replace” field: A set of “Update messages” indicating elements of the data tree whose content is to be replaced
  • “update” field: A set of “Update messages” indicating elements of the data tree whose content is to be updated
  1. gnmic -a 10.73.1.107:6030 --insecure -u arista -p arista get --path "/interfaces/interface[name=Ethernet1]/config/description"
  2. gnmic -a 10.73.1.107:6030 --insecure -u arista -p arista set --update-path "/interfaces/interface[name=Ethernet1]/config/description" --update-value "gnmi-example"
  3. gnmic -a 10.73.1.107:6030 --insecure -u arista -p arista get --path "/interfaces/interface[name=Ethernet1]/config/description"
  4. sh run int et1
  1. gnmic -a 10.73.1.107:6030 --insecure -u arista -p arista set --update "/interfaces/interface[name=Ethernet3]/config/enabled:::bool:::false"
  2. gnmic -a 10.73.1.107:6030 --insecure -u arista -p arista get --path "/interfaces/interface[name=Ethernet3]/config/enabled"
  3. sh run int et3

gNMI Set RPC + PyangBind output

  1. gnmic -a 10.73.1.117:6030 --insecure -u arista -p arista get --path '/network-instances/network-instance[name=default]/protocols/protocol[name=BGP]/bgp'
  1. sh run sec bgp
  1. more test.json
  1. gnmic -a 10.73.1.117:6030 --insecure -u arista -p arista set --replace-path '/network-instances/network-instance[name=default]/protocols/protocol[name=BGP]/bgp' --replace-file test.json
  2. gnmic -a 10.73.1.117:6030 --insecure -u arista -p arista get --path '/network-instances/network-instance[name=default]/protocols/protocol[name=BGP]/bgp'
  1. sh run sec bgp

gNMI Subscribe RPC to OpenConfig paths

Request to the target to stream values for an OpenConfig path

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path '/interfaces/interface[name=Ethernet1]/state/counters'
  2. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path '/network-instances/network-instance/protocols/protocol/bgp/'
  3. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path '/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor[neighbor-address=::133:0:0:2]/state'
  4. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path '/network-instances/network-instance[name=default]/protocols/protocol/bgp/neighbors/neighbor[neighbor-address=172.31.255.8]/state'
  5. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path '/network-instances/network-instance[name=Tenant_B_WAN_Zone]/protocols/protocol[identifier=BGP][name=BGP]/bgp/neighbors/neighbor[neighbor-address=10.255.254.5]/state'
  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --stream-mode "sample" --sample-interval "5s" --path '/network-instances/network-instance[name=default]/protocols/protocol/bgp/neighbors/neighbor[neighbor-address=172.31.255.8]/state'

gNMI Subscribe RPC to EOS native paths

Request to the target to stream values for an EOS native path

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure sub --path "eos_native:/Sysdb/routing/bgp/export/"

gNMI and EOS commands

Get an EOS show command via gNMI

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "cli:/show version"
  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure get --path "cli:/show ip route summary" | jq '.[0].updates[0].values."show ip route summary".totalRoutes'

The above RPC works if the device has this YANG file

You can check this using the Capabilities RPC:

  1. gnmic -a 10.73.1.107:6030 -u arista -p arista --insecure capabilities | grep arista-cli

For more examples about EOS commands and gNMI you can refer to this gist

gNMI configuration file

  1. ls -la
  2. more .gnmic.yml

then

  1. gnmic --config .gnmic.yml subscribe

or

  1. gnmic subscribe

then

  1. more gnmi_output.txt

Generate the paths from a YANG file

  1. cd ../yang_modules/
  2. gnmic path --file openconfig-bgp.yang
  3. gnmic path --file openconfig-bgp.yang | wc -l
  4. gnmic path --file openconfig-bgp.yang --path-type gnmi
  5. gnmic path --file openconfig-bgp.yang --types

pyGNMI

pyGNMI is a Python implementation of the gNMI client

From the root of this repository, move to the pygnmi directory

  1. cd pygnmi

gNMI Capabilities RPC

  1. python3 capabilities.py

gNMI Get RPC

  1. python3 get.py

gNMI Subscribe RPC

  1. python3 sub.py

gNMI Set RPC

Update

  1. python3 update.py

Delete

  1. python3 delete.py

TIG stack

Telegraf is an open source collector written in GO.
Telegraf collects data and writes them into a database.
It is plugin-driven (it has input plugins, output plugins, …)

InfluxDB is an open source time series database written in GO.

Grafana is an open source tool used to visualize time series data.
It supports InfluxDB and other backends.
It runs as a web application.
It is written in GO.

A TIG stack uses:

  • Telegraf to collect data and to write the collected data in InfluxDB.
  • InfluxDB to store the data collected.
  • Grafana to visualize the data stored in InfluxDB

Telegraf plugins

This Telegraf uses the following plugins:

  • gnmi input plugin
  • snmp input plugin
  • Enum processor plugin
  • influxdb output plugin

    Telegraf and gNMI timestamps

Use this telegraf fork in order to have Telegraf to overwrite the gnmi timestamp by its local time
more details https://gist.github.com/ksator/e36a1be086da6c2239c2c2c0eb9fe300

From the root of this repository:

  1. git clone https://github.com/rski/telegraf
  2. cd telegraf
  3. make docker-image
  4. docker images

Check SNMP

We already tested gNMI.

Let’s test SNMP:

  1. snmpwalk --version
  2. snmpwalk -v 2c -c public 10.73.1.107 .1.3.6.1.2.1.1.3.0

Update the required input for the TIG stack

From the root of this repository, move to the TIG directory

  1. cd TIG

Update the input.yml with the devices details:

  1. vi input.yml

Execute this python script to generate:

  • the docker-compose.yml file that starts/stops the TIG stack
  • a Telegraf configuration file for each EOS device in the directory telegraf.d
    1. python3 render.py
    2. more docker-compose.yml
    3. ls telegraf.d/

    Start the TIG stack

    1. docker-compose up -d
    1. docker-compose ps
    2. docker ps
    3. docker images

    Check Telegraf logs

    1. docker logs telegraf

    Check Telegraf configuration

  1. docker exec -it telegraf bash
  1. root@d35fed5663c0:/# ls /etc/telegraf
  2. root@d35fed5663c0:/# more /etc/telegraf/telegraf.conf
  3. root@d35fed5663c0:/# ls /etc/telegraf/telegraf.d
  4. root@d35fed5663c0:/# exit

Query influxdb from CLI

Start an interactive session

  1. docker exec -it influxdb bash
  1. influx

List databases

  1. SHOW DATABASES

Select a database

  1. use arista

List measurements

  1. SHOW MEASUREMENTS

Query ifcounters measurement

  1. SHOW TAG KEYS FROM "ifcounters"
  1. SHOW TAG VALUES FROM "ifcounters" with KEY = "device"
  2. SHOW TAG VALUES FROM "ifcounters" with KEY = "name" WHERE ("device" = 'leaf1')
  1. SELECT * FROM "ifcounters" WHERE "device" = 'leaf1' ORDER BY DESC LIMIT 3
  2. SELECT "in_octets","out_octets", "name" FROM "ifcounters" WHERE "device" = 'leaf1' ORDER BY DESC LIMIT 3
  3. SELECT "in_octets","out_octets", "name" FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name"='Ethernet2' AND time >= now() - 120s)
  4. SELECT "in_octets","out_octets", "name" FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name"=~/Ethernet.*/ AND time >= now() - 120s) GROUP BY "name"
  5. SELECT mean("in_octets")*8 FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name" = 'Ethernet2' AND time >= now() - 10m)
  6. SELECT mean("in_octets")*8 FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name" = 'Ethernet2' AND time >= now() - 10m) GROUP BY time(1m)
  7. SELECT derivative(mean("in_octets"), 1s) *8 FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name" = 'Ethernet2' AND time >= now() - 10m) GROUP BY time(1m)
  8. SELECT derivative(mean("in_unicast_pkts"), 1s) FROM "ifcounters" WHERE ("device" = 'leaf1' AND "name" = 'Ethernet2') AND (time >= now() - 10m) GROUP BY time(1m)
  9. SELECT stddev("in_octets") FROM "ifcounters" WHERE ("device" = 'leaf1' AND ("name" = 'Ethernet1' OR "name" = 'Ethernet2') AND (time >= now() - 10m)) GROUP BY time(1m)
  10. SELECT derivative(stddev("out_octets"), 1s) / 8 FROM "ifcounters" WHERE ("device" =~ /lea.*/ AND "name" =~ /Ethernet[1|2]/ AND (time >= now() - 10m)) GROUP BY time(1m), "device"

Query openconfig_bgp measurement

  1. SHOW TAG KEYS FROM "openconfig_bgp"
  1. SHOW TAG VALUES FROM "openconfig_bgp" WITH KEY = "device"
  2. SHOW TAG VALUES FROM "openconfig_bgp" WITH KEY = "name"
  3. SHOW TAG VALUES FROM "openconfig_bgp" WITH KEY = "name" WHERE "device"='leaf1'
  4. SHOW TAG VALUES FROM "openconfig_bgp" WITH KEY = "neighbor_address" WHERE "device"='leaf1'
  5. SHOW TAG VALUES FROM "openconfig_bgp" WITH KEY = "neighbor_address" WHERE ("device"='leaf1' AND "name" = 'default')
  1. SELECT LAST("neighbors/neighbor/state/session_state") FROM "openconfig_bgp" WHERE ("device"='leaf1' AND "neighbor_address" = '10.255.254.1')
  2. SELECT LAST("neighbors/neighbor/state/session_state") FROM "openconfig_bgp" WHERE ("device"='leaf1') GROUP BY neighbor_address
  3. SELECT LAST("neighbors/neighbor/state/session_state") FROM "openconfig_bgp" WHERE ("device"='leaf1' AND "name" = 'default') GROUP BY neighbor_address
  4. SELECT LAST("neighbors/neighbor/state/session_state") AS session_state FROM "openconfig_bgp" WHERE "name" = 'default' GROUP BY "device", "neighbor_address"
  5. SELECT "device", "neighbor_address", LAST("neighbors/neighbor/state/session_state") AS session_state FROM "openconfig_bgp" WHERE ("device"='leaf1' AND "neighbor_address" = '10.255.254.1')
  1. SELECT "neighbors/neighbor/afi_safis/afi_safi/state/prefixes/received" FROM "openconfig_bgp" WHERE ("device" = 'leaf1' AND "neighbor_address"='10.255.254.1' AND "afi_safi_name"='IPV4_UNICAST') LIMIT 15
  2. SELECT mean("neighbors/neighbor/afi_safis/afi_safi/state/prefixes/sent") FROM "openconfig_bgp" WHERE ("device" = 'leaf1' AND "afi_safi_name" = 'IPV4_UNICAST' AND "name" = 'default') GROUP BY time(1m), "neighbor_address"
  3. SELECT mean("neighbors/neighbor/afi_safis/afi_safi/state/prefixes/sent") FROM "openconfig_bgp" WHERE ("device" = 'leaf1' AND "name" = 'default') GROUP BY time(1m), "neighbor_address", "afi_safi_name"
  1. SELECT mean("neighbors/neighbor/state/messages/sent/UPDATE") FROM "openconfig_bgp" WHERE ("device" = 'leaf1' AND "name" = 'default' AND time >= now() - 10m) GROUP BY "neighbor_address",time(1m)
  1. SELECT COUNT(*) FROM (SELECT LAST("neighbors/neighbor/config/neighbor_address") FROM "openconfig_bgp" GROUP BY "neighbor_address") GROUP BY "device"
  1. exit
  2. exit

Query influxdb from python

  1. pip3 freeze | grep influxdb
  1. from influxdb import InfluxDBClient
  2. influx_client = InfluxDBClient('localhost',8086)
  3. influx_client.query('show databases')
  4. influx_client.query('show measurements', database='arista')
  5. points = influx_client.query("""SELECT "in_octets" FROM "ifcounters" WHERE ("device"='leaf1' AND "name"='Ethernet1') ORDER BY DESC LIMIT 3""", database='arista').get_points()
  6. for point in points:
  7. print(point['in_octets'])
  8. exit()

Check Grafana

The datasource is already configured. It uses InfluxDB.

We loaded ready to use dashboards.

We loaded a plugin.

  1. docker exec -it grafana bash
  1. bash-5.0$ more /etc/grafana/provisioning/datasources/datasource.yaml
  2. bash-5.0$ more /etc/grafana/provisioning/dashboards/dashboards.yaml
  3. bash-5.0$ ls /var/tmp/dashboards/
  4. bash-5.0$ ls /var/lib/grafana/plugins
  5. bash-5.0$ exit

Use Grafana GUI

We can now use the Grafana GUI.
The default username and password are admin/admin, but we changed them to arista/arista.
The default port is 3000.

http://IP:3000/login

We can use the dashboards we already loaded or we can create new dashborads querying influxDB.

Stop the TIG stack

  1. docker-compose down
  1. docker ps