Hello my friend,
We hope you have aa good festive period, celebrated Christmas and New Year with your family and friends, and started the new year. Some time ago we’ve discussed a comparison of NAPALM and OpenConfig at a high level and promised to share some details. Today you will see the details of data collection with NAPALM and NETCONF with OpenConfig YANG modules.
1
2
3
4
5 No part of this blogpost could be reproduced, stored in a
retrieval system, or transmitted in any form or by any
means, electronic, mechanical or photocopying, recording,
or otherwise, for commercial purposes without the
prior permission of the author.
Where to Start with Automation?
Recently we have had an interesting webinar together with Rick Donato, my colleague from a Network Automation Industry. During the webinar we mapped various products existing in the automation (not only network, but in general) space and, you know what, there is indeed a huge amount of products. Therefore, when you are already a savvy in the automation world, you can benefit from them a lot. However, if you are just making first steps, you will be totally lost.
We want you to be a beneficiary of network automation, not a loser. Therefore, we are here to help you with our world-class network automation trainings, where you can start your journey without any knowledge in automation or coding.
We offer the following training programs:
- Zero-to-Hero Network Automation Training
- Automation with Nornir (2nd step after advanced network automation)
- Automation Orchestration wth AWX
During these trainings you will learn the following topics:
- Success and failure strategies to build the automation tools.
- Principles of software developments and tools for that.
- Data encoding (free-text, XML, JSON, YAML, Protobuf)
- Model-driven network automation with YANG, NETCONF, RESTCONF, GNMI.
- Full configuration templating with Jinja2 based on the source of truth (NetBox).
- Best programming languages (Python, Bash) for developing automation, configuration management tools (Ansible) and automation frameworks (Nornir).
- Network automation infrastructure (Linux, Linux networking, KVM, Docker).
- Orchestration of automation workflows with AWX and its integration with NetBox, GitHub, as well as custom execution environments for better scalability.
I would recommend the Network Automation Training to anyone who has been working in the industry for a period of time and prepare to become the next generation of network engineers.
Eric Leung @ Bloomberg
Moreover, we put all mentions technologies in the context of the real use cases, which our team has solved and are solving in various projects in the service providers, enterprise and data centre networks and systems across the Europe and USA. That gives you opportunity to ask questions to understand the solutions in-depts and have discussions about your own projects. And on top of that, each technology is provided with online demos and you are doing the lab afterwards to master your skills. Such a mixture creates a unique learning environment, which all students value so much. Join us and unleash your potential.
Brief Description
In this blogpost we focus on Cisco IOS XR and Arista EOS, as these network operating systems are supported in NAPALM natively. Nokia SR OS will be covered separately including installation of a NAPALM community driver for Nokia SR OS.
In the previous blogpost about NAPALM and OpenConfig we have explained the fundamental difference between those two approaches. To recap them:
- NAPALM interacts with network devices based on some device specific interface (e.g., eAPI for Arista EOS and XML for Cisco IOS XR) and then post-process collected data to normalise it and render in the vendor-agnostic format based on predefined data sets (e.g., interfaces, ARP tables, BGP tables, etc). Some data (e.g., configuration) stays vendor-specific and is represented as a text file.
- OpenConfig is a collection of vendor-agnostic YANG modules, meaning that they are implemented in the network operating system and interaction with network devices done in a vendor-agnostic approach using available transport (NETCONF, RESTCONF, or gNMI). There is no post-processing needed; however, OpenConfig must be supported by the operating system or you won’t be able to use it.
Both those projects started back in 2015 approximately, but went different routes due to different authors’ backgrounds:
- NAPALM was created by a group of enthusiasts led by David Barroso trying to solve the multivendor network management coming from a specific network and possibility of a specific company.
- OpenConfig was created by Google to manage Google data centres heavily relying on the Google buying power: Goole is, perhaps, the biggest network worldwide with thousands new devices needed regularly; therefore, multimillion investments are needed, which help to persuade vendors to implement OpenConfig in vendors’ network operating systems.
Lab Setup
We’ll use the following lab topology in order to show the operation of NAPALM-based and NETCONF/OpenConfig-based automation:
As said beforehand, today we’ll focus on Cisco IOS XR and Arista EOS based devices.
Software components used in this lab has following versions:
- Debian 11.1
- Python 3.9
- napalm 3.3.1
- ncclient 0.6.12
- Cisco IOS XR 6.5.1
- Arista EOS 4.26.0.1F
When you install ncclient > 0.6.9, you will see error in pip, that junos napalm driver requires 0.6.9. We don’t use it, so not a problem for our setup, but be mindful in your environment.
Scenario Description
In today’s blogpost, we’ll focus on the following tasks:
- How to collect an operational state of interfaces with NAPALM in a vendor-agnostic format?
- How to collect a content of ARP tables with NAPALM in a vendor-agnostic format?
- How to collect a configuration of interfaces with NAPALM (that will be in a vendor-specific format)?
- How to collect an operational state of interfaces with OpenConfig/YANG over NETCONF in a vendor-agnostic format?
- How to collect a content of ARP tables with OpenConfig/YANG over NETCONF in a vendor-agnostic format?
- How to collect a configuration of interfaces with OpenConfig/YANG over NETCONF in a vendor-agnostic format?
We will use Python as our automation tool in all the cases. At the end, as usual, we’ll do some comparison.
Enrol to our Network Automation Trainings to boost you from a newbie to an expert level.
Part #1. Solution with NAPALM
Step #1.1. NAPALM Installation
First of all, we’ll focus on the NAPALM solution. To begin with, install it using pip:
1 $ pip install napalm
All Python packages are installed inside the virtual environment. It is expected that you know what it is and how to use it. Our training will teach you this and other Python tricks.
Step #1.2. Prepare Network Devices to Interact with NAPALM
The next step is to prepare network devices to work with NAPALM. In the official documentation to NAPALM you will find that:
- To manage Arista EOS eAPI will be used.
- To manage Cisco IOS XR XML-agent will be used.
It is suggested that it is possible to use NETCONF for Cisco IOS XR starting with 7.* version. As we have an older version, we don’t use it.
Step #1.2.1. Configure Arista EOS for NAPALM
In order to enable eAPI, you need to perform the following configuration:
1
2
3
4
5
6
7
8 eos1#conf t
eos1(config)#management security
eos1(config-mgmt-security)# ssl profile RC_PROTECT
eos1(config-mgmt-sec-ssl-profile-RC_PROTECT)# certificate RC_CERT key RC_KEY
eos1(config-mgmt-sec-ssl-profile-RC_PROTECT)#
eos1(config-mgmt-sec-ssl-profile-RC_PROTECT)#management api http-commands
eos1(config-mgmt-api-http-cmds)# protocol https ssl profile RC_PROTECT
eos1(config-mgmt-api-http-cmds)# no shutdown
In this snippet:
- We create and SSL profile to encrypt the HTTP session.
- We enable eAPI over HTTPS and use the created profile.
The prerequisite to create profile is to have key and certificate. The self-signed would work.
Learn how to create a self-signed certificate in Arista EOS in one of the pyGNMI blogposts.
Validate that eAPI is working:
1
2
3
4
5
6
7
8 eos1#show management api http-commands
Enabled: Yes
HTTPS server: running, set to use port 443
HTTP server: shutdown, set to use port 80
Local HTTP server: shutdown, no authentication, set to use port 8080
Unix Socket server: shutdown, no authentication
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY
Step #1.2.1. Configure Cisco IOS XR for NAPALM
In order to enable XML-agent in Cisco IOS XR, use the following configuration:
1
2
3
4
5
6
7
8 RP/0/0/CPU0:xr1(config)#show configuration
Sun Jan 16 10:53:12.651 UTC
Building configuration...
!! IOS XR Configuration 6.5.1
xml agent tty
iteration off
!
end
Step #1.3. Develop a Python Script with NAPALM
Once your Linux host has Python and NAPALM and your network devices are enabled to work with NAPALM, we can create a simple script to collect all the information we’ve mentioned above in the solution description part:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 $ cat 01_collect_napalm.py
#!/usr/bin/env python
# Modules
import napalm
import datetime
import json
# Local modules
from bin.inventory import get_inventory
# Vars
path_inventory_str = "./inventory"
# Body
if __name__ == "__main__":
## Get inventory data
inventory_dict = get_inventory(path_str=path_inventory_str)
## Collect data
for hostname_str, hostvar_dict in inventory_dict.items():
## Get inital timestamp
t1 = datetime.datetime.now()
## Get driver
driver_obj = napalm.get_network_driver(name=hostvar_dict["nos"])
## Connect to device
with driver_obj(hostname=hostvar_dict["ip_address"], username=hostvar_dict["username"],
password=hostvar_dict["password"]) as device_obj:
print("Interfaces operational data:\n")
print(json.dumps(device_obj.get_interfaces(), indent=4))
print("\n\nARP table:")
print(json.dumps(device_obj.get_arp_table(), indent=4))
print("\n\nConfig:")
print(json.dumps(device_obj.get_config(), indent=4))
## Get final timestamp
t2 = datetime.datetime.now()
## Print time
print("=" * 84)
print("Completed in", str(t2 - t1), "for", hostname_str)
print("=" * 84)
Link towards corresponding GitHub repo with examples you would find at the end of this blogpost.
The Python code contains comments to simplify understanding, but we’ll provide some further details:
- You’d import all necessary libraries:
- Obviously, napalm itself, for automation.
- datetime to make timestamps to measure the performance.
- json in order to modify the output of the data collected via napalm to simplify reading the results.
- Auxiliary function get_inventory() to convert local YAML files into Python dictionary. In your network you may have different file structure or use a centralised application (like, NetBox).
- Path towards inventory directory.
- In the body part, you would:
- Import inventory data.
- Run a loop over all the devices in the inventory. At this stage we are not after the performance, but more after functionality. In the production-grade setup we would re-write this part with threading using ThreadPoolExecutor() class.
- Collect a timestamp t1 before we start working with a device via NAPALM.
- Create an object driver_obj using function get_network_driver() from napalm module. The argument is a name of the device’s network operating system per the official NAPALM documentation.
- Using the context manager with … as … create an object device_obj based out of the driver_obj augmenting it with the connectivity data.
- Using methods of a created object collect the relevant data:
- get_interfaces() collects the high-level operational data of the network interfaces in a vendor-agnostic format.
- get_arp_table() collects the content of the ARP table in a vendor-agnostic format.
- get_config() collects the content of the configuration files (running, startup, candidate) in vendor-specific format.
- Print the collected information using print() function pre-processing it by dumps() function from the json module for a better readability.
- Collect another timestamp t2 after finishing all the data collection.
- Print the the elapsed timeframe.
To give you a better understanding of the input inventory data, take a look at a content of one of the files:
1
2
3
4
5
6
7 $ cat inventory/xr1.yaml
---
ip_address: 192.168.101.12
nos: iosxr
username: zthnat
password: tanhtz0!
...
Step #1.4. Execution of Python Script with NAPALM
It is time now to check, whether our script works as expected:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 $ python 01_collect_napalm.py
Interfaces operational data:
{
"GigabitEthernet0/0/0/0": {
"is_enabled": true,
"is_up": true,
"mac_address": "A6:F7:49:A9:37:B4",
"description": "xr1_g0/0/0/0-sr1_1/1/c2/1",
"speed": 1000,
"last_flapped": -1.0,
"mtu": 1514
},
"GigabitEthernet0/0/0/1": {
"is_enabled": true,
"is_up": true,
"mac_address": "26:99:94:56:20:C1",
"description": "xr1_g0/0/0/1-vx1_swp2",
"speed": 1000,
"last_flapped": -1.0,
"mtu": 1514
},
"GigabitEthernet0/0/0/2": {
"is_enabled": true,
"is_up": true,
"mac_address": "C6:B0:B5:E7:8B:E1",
"description": "xr1_g0/0/0/2-eos1_eth1",
"speed": 1000,
"last_flapped": -1.0,
"mtu": 1514
},
"GigabitEthernet0/0/0/3": {
"is_enabled": false,
"is_up": false,
"mac_address": "42:A5:79:97:06:35",
"description": "",
"speed": 1000,
"last_flapped": -1.0,
"mtu": 1514
},
"Loopback0": {
"is_enabled": true,
"is_up": true,
"mac_address": "",
"description": "",
"speed": 0,
"last_flapped": -1.0,
"mtu": 1500
},
"Loopback1": {
"is_enabled": true,
"is_up": true,
"mac_address": "",
"description": "",
"speed": 0,
"last_flapped": -1.0,
"mtu": 1500
},
"Loopback2": {
"is_enabled": true,
"is_up": true,
"mac_address": "",
"description": "",
"speed": 0,
"last_flapped": -1.0,
"mtu": 1500
},
"MgmtEth0/0/CPU0/0": {
"is_enabled": true,
"is_up": true,
"mac_address": "B6:CD:83:84:99:A1",
"description": "",
"speed": 1000,
"last_flapped": -1.0,
"mtu": 1514
},
"Null0": {
"is_enabled": true,
"is_up": true,
"mac_address": "",
"description": "",
"speed": 0,
"last_flapped": -1.0,
"mtu": 1500
}
}
ARP table:
[
{
"interface": "GigabitEthernet0/0/0/0",
"mac": "3A:98:DE:FE:0E:86",
"ip": "10.0.0.0",
"age": 1531.0
},
{
"interface": "GigabitEthernet0/0/0/0",
"mac": "A6:F7:49:A9:37:B4",
"ip": "10.0.0.1",
"age": -1.0
},
{
"interface": "GigabitEthernet0/0/0/1",
"mac": "26:99:94:56:20:C1",
"ip": "10.0.0.4",
"age": -1.0
},
{
"interface": "GigabitEthernet0/0/0/1",
"mac": "5A:2C:51:CB:91:77",
"ip": "10.0.0.5",
"age": 216.0
},
{
"interface": "GigabitEthernet0/0/0/2",
"mac": "C6:B0:B5:E7:8B:E1",
"ip": "10.0.0.6",
"age": -1.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "22:5D:0D:BA:B2:16",
"ip": "192.168.51.151",
"age": 6736.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "DA:7A:6D:BF:7F:CA",
"ip": "192.168.101.1",
"age": 436.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "AE:3E:E2:1D:AB:0B",
"ip": "192.168.101.11",
"age": 2821.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "B6:CD:83:84:99:A1",
"ip": "192.168.101.12",
"age": -1.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "26:55:A9:EB:16:42",
"ip": "192.168.101.13",
"age": 251.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "5E:1F:22:C7:05:1C",
"ip": "192.168.101.21",
"age": 11014.0
},
{
"interface": "MgmtEth0/0/CPU0/0",
"mac": "7E:C5:65:23:3A:5B",
"ip": "192.168.101.152",
"age": 2945.0
}
]
Config:
{
"startup": "",
"running": "\n\n!! Last configuration change at Sun Jan 16 10:53:15 2022 by cisco\n!\nhostname xr1\ntelnet vrf mgmt ipv4 server max-servers 1\nvrf mgmt\n address-family ipv4 unicast\n !\n address-family ipv6 unicast\n !\n!\ncontrol-plane\n management-plane\n out-of-band\n vrf mgmt\n interface MgmtEth0/0/CPU0/0\n allow all\n !\n !\n !\n!\ninterface Loopback0\n ipv4 address 10.0.255.22 255.255.255.255\n!\ninterface Loopback1\n!\ninterface Loopback2\n ipv4 address 10.0.253.22 255.255.255.255\n!\ninterface MgmtEth0/0/CPU0/0\n vrf mgmt\n ipv4 address 192.168.101.12 255.255.255.0\n ipv6 address fc00:192:168:101::12/64\n!\ninterface GigabitEthernet0/0/0/0\n description xr1_g0/0/0/0-sr1_1/1/c2/1 \n mtu 1514\n ipv4 address 10.0.0.1 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/1\n description xr1_g0/0/0/1-vx1_swp2 \n mtu 1514\n ipv4 address 10.0.0.4 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/2\n description xr1_g0/0/0/2-eos1_eth1 \n mtu 1514\n ipv4 address 10.0.0.6 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/3\n shutdown\n!\nrouter static\n vrf mgmt\n address-family ipv4 unicast\n 0.0.0.0/0 192.168.101.1\n !\n address-family ipv6 unicast\n ::/0 fc00:192:168:101::1\n !\n !\n!\nxml agent tty\n iteration off\n!\nnetconf agent tty\n!\nnetconf-yang agent\n ssh\n!\nssh server vrf mgmt\nssh server netconf vrf mgmt\nend",
"candidate": "\n\n!! Last configuration change at Sun Jan 16 10:53:15 2022 by cisco\n!\nhostname xr1\ntelnet vrf mgmt ipv4 server max-servers 1\nvrf mgmt\n address-family ipv4 unicast\n !\n address-family ipv6 unicast\n !\n!\ncontrol-plane\n management-plane\n out-of-band\n vrf mgmt\n interface MgmtEth0/0/CPU0/0\n allow all\n !\n !\n !\n!\ninterface Loopback0\n ipv4 address 10.0.255.22 255.255.255.255\n!\ninterface Loopback1\n!\ninterface Loopback2\n ipv4 address 10.0.253.22 255.255.255.255\n!\ninterface MgmtEth0/0/CPU0/0\n vrf mgmt\n ipv4 address 192.168.101.12 255.255.255.0\n ipv6 address fc00:192:168:101::12/64\n!\ninterface GigabitEthernet0/0/0/0\n description xr1_g0/0/0/0-sr1_1/1/c2/1 \n mtu 1514\n ipv4 address 10.0.0.1 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/1\n description xr1_g0/0/0/1-vx1_swp2 \n mtu 1514\n ipv4 address 10.0.0.4 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/2\n description xr1_g0/0/0/2-eos1_eth1 \n mtu 1514\n ipv4 address 10.0.0.6 255.255.255.254\n!\ninterface GigabitEthernet0/0/0/3\n shutdown\n!\nrouter static\n vrf mgmt\n address-family ipv4 unicast\n 0.0.0.0/0 192.168.101.1\n !\n address-family ipv6 unicast\n ::/0 fc00:192:168:101::1\n !\n !\n!\nxml agent tty\n iteration off\n!\nnetconf agent tty\n!\nnetconf-yang agent\n ssh\n!\nssh server vrf mgmt\nssh server netconf vrf mgmt\nend"
}
====================================================================================
Completed in 0:00:11.537944 for xr1
====================================================================================
Interfaces operational data:
{
"Management1": {
"is_up": true,
"is_enabled": true,
"description": "",
"last_flapped": 1635005976.8912313,
"mtu": 1500,
"speed": 1000,
"mac_address": "26:55:A9:EB:16:42"
},
"Loopback0": {
"is_up": true,
"is_enabled": true,
"description": "",
"last_flapped": 1636804818.5330117,
"mtu": 65535,
"speed": 0,
"mac_address": ""
},
"Loopback1": {
"is_up": true,
"is_enabled": true,
"description": "",
"last_flapped": 1638837717.2345128,
"mtu": 65535,
"speed": 0,
"mac_address": ""
},
"Ethernet2": {
"is_up": true,
"is_enabled": true,
"description": "eos1_eth2-vx1_swp3",
"last_flapped": 1635005984.0712812,
"mtu": 1500,
"speed": 1000,
"mac_address": "0E:90:04:E2:13:8C"
},
"Ethernet3": {
"is_up": true,
"is_enabled": true,
"description": "",
"last_flapped": 1635596618.8403418,
"mtu": 1500,
"speed": 1000,
"mac_address": "0E:90:04:E2:13:8C"
},
"Ethernet1": {
"is_up": true,
"is_enabled": true,
"description": "eos1_eth1-xr1_g0/0/0/2",
"last_flapped": 1635005984.249793,
"mtu": 1500,
"speed": 1000,
"mac_address": "0E:90:04:E2:13:8C"
}
}
ARP table:
[
{
"interface": "Management1",
"mac": "DA:7A:6D:BF:7F:CA",
"ip": "192.168.101.1",
"age": 0.0
},
{
"interface": "Management1",
"mac": "B6:CD:83:84:99:A1",
"ip": "192.168.101.12",
"age": 252.0
}
]
Config:
{
"startup": "! Command: show startup-config\n! Startup-config last modified at Sun Jan 16 11:02:53 2022 by zthnat\n! device: eos1 (vEOS, EOS-4.26.0.1F)\n!\n! boot system flash:/vEOS-lab.swi\n!\nswitchport default mode routed\n!\ntransceiver qsfp default-mode 4x10G\n!\nservice routing protocols model ribd\n!\nhostname eos1\n!\nspanning-tree mode mstp\n!\naaa authorization exec default local\n!\nno aaa root\n!\nusername zthnat privilege 15 secret sha512 $6$7aNvwBMhei658OJO$//vFuTvsMycaViLiSSYdjtYsyyetoNh5hiWSB8jSWx4oZDXRiDnik5cc16ZcdN8pLGHzPRJYirssJSaoyU66p0\n!\ninterface Ethernet1\n description eos1_eth1-xr1_g0/0/0/2\n no switchport\n ip address 10.0.0.7/31\n!\ninterface Ethernet2\n description eos1_eth2-vx1_swp3\n no switchport\n ip address 10.0.0.8/31\n!\ninterface Ethernet3\n no switchport\n!\ninterface Loopback0\n ip address 10.0.255.33/32\n!\ninterface Loopback1\n ip address 10.0.254.33/32\n!\ninterface Management1\n ip address 192.168.101.13/24\n ipv6 address fc00:192:168:101::13/64\n!\nip routing\n!\nipv6 unicast-routing\n!\nip route 0.0.0.0/0 192.168.101.1\n!\nipv6 route ::/0 fc00:192:168:101::1\n!\nmanagement api http-commands\n protocol https ssl profile RC_PROTECT\n no shutdown\n!\nmanagement api gnmi\n transport grpc def\n port 57400\n!\nmanagement api netconf\n transport ssh def\n!\nmanagement api restconf\n transport https def\n ssl profile zthnat_restconf\n!\nmanagement security\n ssl profile RC_PROTECT\n certificate RC_CERT key RC_KEY\n !\n ssl profile zthnat_restconf\n certificate zthnat_restconf.crt key zthnat_restconf.key\n!\nend\n",
"running": "! Command: show running-config\n! device: eos1 (vEOS, EOS-4.26.0.1F)\n!\n! boot system flash:/vEOS-lab.swi\n!\nswitchport default mode routed\n!\ntransceiver qsfp default-mode 4x10G\n!\nservice routing protocols model ribd\n!\nhostname eos1\n!\nspanning-tree mode mstp\n!\naaa authorization exec default local\n!\nno aaa root\n!\nusername zthnat privilege 15 secret sha512 $6$7aNvwBMhei658OJO$//vFuTvsMycaViLiSSYdjtYsyyetoNh5hiWSB8jSWx4oZDXRiDnik5cc16ZcdN8pLGHzPRJYirssJSaoyU66p0\n!\ninterface Ethernet1\n description eos1_eth1-xr1_g0/0/0/2\n no switchport\n ip address 10.0.0.7/31\n!\ninterface Ethernet2\n description eos1_eth2-vx1_swp3\n no switchport\n ip address 10.0.0.8/31\n!\ninterface Ethernet3\n no switchport\n!\ninterface Loopback0\n ip address 10.0.255.33/32\n!\ninterface Loopback1\n ip address 10.0.254.33/32\n!\ninterface Management1\n ip address 192.168.101.13/24\n ipv6 address fc00:192:168:101::13/64\n!\nip routing\n!\nipv6 unicast-routing\n!\nip route 0.0.0.0/0 192.168.101.1\n!\nipv6 route ::/0 fc00:192:168:101::1\n!\nmanagement api http-commands\n protocol https ssl profile RC_PROTECT\n no shutdown\n!\nmanagement api gnmi\n transport grpc def\n port 57400\n!\nmanagement api netconf\n transport ssh def\n!\nmanagement api restconf\n transport https def\n ssl profile zthnat_restconf\n!\nmanagement security\n ssl profile RC_PROTECT\n certificate RC_CERT key RC_KEY\n !\n ssl profile zthnat_restconf\n certificate zthnat_restconf.crt key zthnat_restconf.key\n!\nend\n",
"candidate": ""
}
====================================================================================
Completed in 0:00:00.740267 for eos1
====================================================================================
The first obviously good thing, that our Python script for multivendor network automation with NAPALM works. Let’s analyse the gathered output:
- An interfaces’ output shows basic parameters, such as administrative and operational states, description, MTU, MAC address, speed, and a time since the last flap. It does so in a multivendor fashion, as the output structure for Cisco IOS XR and for Arista EOS are exactly the same.
- An ARP table output shows the list of the entries in the ARP tables including an IP address, associated MAC address, interface’s name and an age of the entry. Again, it does so in the vendor-agnostic format; however, there is a difference between Cisco IOS XR and Arista EOS: in the output from the Cisco IOS XR device the ARP tables contains local IP and MAC addresses of Cisco OS XR itself, whereas for Arista EOS this information is missing.
- An output of the configuration contains three keys: running, startup and candidate. For Cisco IOS XR, the candidate and the running ones have the same value by default, whereas the startup one being empty. For Arista EOS, the startup and the running ones are the same, whereas the candidate is empty. Note: in the scenario description we mentioned that we want to collect the configuration of network interfaces. In this scenario we are collecting the full configuration, as there the configuration file is just a flat text file and NAPLAM doesn’t normalise it.
So far NAPALM part is completed and we can move on to OpenConfig/YANG with NETCONF transport.
Part #2. Solution with OpenConfig/YANG over NETCONF
Step #2.1. NETCONF Client Installation
Following the same logic we did before for NAPALM, let’s install a Python package to work with NETCONF
1 $ pip install --upgrade ncclient
As a matter of a fact, ncclient is installed as a dependency of napalm. However, it is installed in a form of an old version 0.6.9. Therefore, we upgrade it to the newest one.
Besides that, we will install another library xmltodict, which allows us to convert the XML-like string into a Python dictionary.
1 $ pip install xmltodict
Step #2.2. Prepare Network Devices to Interact with NETCONF
There is no specific configuration needed to at Cisco IOS XR or Arista EOS to work with OpenConfig besides general NETCONF configuration, as OpenConfig is a collection of YANG modules, which either exist in the network operating system already or not.
Step #2.2.1. Configure Arista EOS for NETCONF
The following configuration shall be applied at Arista EOS to enable NETCONF:
1
2
3 eos1#conf t
eos1(config)#management api netconf
eos1(config-mgmt-api-netconf)# transport ssh def
The following commands helps you to validate it is up and running:
1
2
3 eos1#show management api netconf
Enabled: Yes
Server: running on port 830, in default VRF
Step #2.2.1. Configure Cisco IOS XR for NETCONF
The following configuration shall be applied at Arista EOS to enable NETCONF:
1
2
3
4
5
6
7
8
9
10
11 xr1(config)#show conf
Sun Jan 16 19:42:42.464 UTC
Building configuration...
!! IOS XR Configuration 6.5.1
netconf agent tty
!
netconf-yang agent
ssh
!
ssh server netconf vrf mgmt
end
Step #2.3. Develop a Python Script with NETCONF and OpenConfig YANG modules
Now it is time to create our Python script for multivendor network automaton. Its logic is broadly the repetition of the NAPALM one, but with some modifications:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #!/usr/bin/env python
# Modules
import datetime
from ncclient import manager
import xmltodict
import json
# Local modules
from bin.inventory import get_inventory
# Vars
path_inventory_str = "./inventory"
# Body
if __name__ == "__main__":
## Get inventory data
inventory_dict = get_inventory(path_str=path_inventory_str)
## Collect data
for hostname_str, hostvar_dict in inventory_dict.items():
## Get inital timestamp
t1 = datetime.datetime.now()
## Get driver
device_params_dict = {"name": hostvar_dict["nos"] if hostvar_dict["nos"] == "iosxr" else "default"}
## Connect to device
with manager.connect(host=hostvar_dict["ip_address"], username=hostvar_dict["username"],
password=hostvar_dict["password"], device_params=device_params_dict) as device_obj:
filter_str = "<filter type="subtree"><interfaces xmlns="http://openconfig.net/yang/interfaces"/></filter>"
r1_str = str(device_obj.get(filter=filter_str))
print("Interfaces operational and config data including ARP:\n")
print(json.dumps(xmltodict.parse(r1_str), indent=4))
## Get final timestamp
t2 = datetime.datetime.now()
## Print time
print("=" * 84)
print("Completed in", str(t2 - t1), "for", hostname_str)
print("=" * 84)
As said before, the script logic replicates the one with NAPALM, so we will point out differences:
- We use manager() class from ncclient module to build the object, which is used to interact with the network device. All the connectivity details as well as type of the operating system per the official ncclient documentation are submitted to this class.
- Per OpenConfig YANG modules, all configuration and operational data for a certain piece of configuration is defined by the same YANG module. As we are after interfaces configuration, we use openconfig-interfaces YANG module. Luckily, it also contains the ARP data, as an attribute of configuration and operational data of the interface.
- As such, we do a single collection using a get() method providing the XML-formatted string with YANG module and a top-level container as an input argument.
- The response is converted to a string using str() function.
- Finally, it is converted to a Python dictionary using the parse() function from xmltodict module.
The inventory files are the same, so that bit stays unchanged.
Step #2.4. Execution of Python Script with OpenConfig/YANG over NETCONF
Yet again, let’s run our new Python script for multivendor network automation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 $ python 01_collect_openconfig.py
Interfaces operational and config data including ARP:
{
"rpc-reply": {
"@message-id": "urn:uuid:d152be0d-42a0-481d-aee2-ab2573d0174b",
"@xmlns:nc": "urn:ietf:params:xml:ns:netconf:base:1.0",
"@xmlns": "urn:ietf:params:xml:ns:netconf:base:1.0",
"data": {
"interfaces": {
"@xmlns": "http://openconfig.net/yang/interfaces",
"interface": [
{
"name": "Loopback0",
"config": {
"name": "Loopback0",
"type": {
"@xmlns:idx": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "idx:softwareLoopback"
},
"enabled": "true"
},
"state": {
"name": "Loopback0",
"enabled": "true",
"admin-status": "UP",
"oper-status": "UP",
"last-change": "4432368",
"ifindex": "8",
"mtu": "1500",
"type": {
"@xmlns:idx": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "idx:softwareLoopback"
}
},
"subinterfaces": {
"subinterface": {
"index": "0",
"ipv4": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "10.0.255.22",
"config": {
"ip": "10.0.255.22",
"prefix-length": "32"
},
"state": {
"ip": "10.0.255.22",
"prefix-length": "32",
"origin": "STATIC"
}
}
}
}
}
}
},
{
"name": "MgmtEth0/0/CPU0/0",
"config": {
"name": "MgmtEth0/0/CPU0/0",
"type": {
"@xmlns:idx": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "idx:ethernetCsmacd"
},
"enabled": "true"
},
"state": {
"name": "MgmtEth0/0/CPU0/0",
"enabled": "true",
"admin-status": "UP",
"oper-status": "UP",
"last-change": "7358931",
"counters": {
"in-unicast-pkts": "112698",
"in-octets": "58871781",
"out-unicast-pkts": "110584",
"out-octets": "23101255",
"in-multicast-pkts": "218399",
"in-broadcast-pkts": "885",
"out-multicast-pkts": "41029",
"out-broadcast-pkts": "2",
"out-discards": "0",
"in-discards": "0",
"in-unknown-protos": "0",
"in-errors": "0",
"out-errors": "0",
"last-clear": "2021-10-23T16:16:33.600+00:00"
},
"ifindex": "7",
"mtu": "1514",
"type": {
"@xmlns:idx": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "idx:ethernetCsmacd"
}
},
"ethernet": {
"@xmlns": "http://openconfig.net/yang/interfaces/ethernet",
"config": {
"auto-negotiate": "false"
},
"state": {
"auto-negotiate": "false",
"hw-mac-address": "b6:cd:83:84:99:a1",
"counters": {
"in-8021q-frames": "0",
"in-mac-pause-frames": "0",
"in-oversize-frames": "0",
"in-jabber-frames": "0",
"in-fragment-frames": "0",
"in-crc-errors": "0",
"out-8021q-frames": "0",
"out-mac-pause-frames": "0"
}
}
},
"subinterfaces": {
"subinterface": {
"index": "0",
"ipv4": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "192.168.101.12",
"config": {
"ip": "192.168.101.12",
"prefix-length": "24"
},
"state": {
"ip": "192.168.101.12",
"prefix-length": "24",
"origin": "STATIC"
}
}
},
"neighbors": {
"neighbor": [
{
"ip": "192.168.51.151",
"state": {
"ip": "192.168.51.151",
"origin": "DYNAMIC",
"link-layer-address": "22:5d:0d:ba:b2:16"
}
},
{
"ip": "192.168.101.1",
"state": {
"ip": "192.168.101.1",
"origin": "DYNAMIC",
"link-layer-address": "da:7a:6d:bf:7f:ca"
}
},
{
"ip": "192.168.101.11",
"state": {
"ip": "192.168.101.11",
"origin": "DYNAMIC",
"link-layer-address": "ae:3e:e2:1d:ab:0b"
}
},
{
"ip": "192.168.101.12",
"state": {
"ip": "192.168.101.12",
"origin": "OTHER",
"link-layer-address": "b6:cd:83:84:99:a1"
}
},
{
"ip": "192.168.101.13",
"state": {
"ip": "192.168.101.13",
"origin": "DYNAMIC",
"link-layer-address": "26:55:a9:eb:16:42"
}
},
{
"ip": "192.168.101.21",
"state": {
"ip": "192.168.101.21",
"origin": "DYNAMIC",
"link-layer-address": "5e:1f:22:c7:05:1c"
}
},
{
"ip": "192.168.101.152",
"state": {
"ip": "192.168.101.152",
"origin": "DYNAMIC",
"link-layer-address": "7e:c5:65:23:3a:5b"
}
}
]
}
},
"ipv6": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "fc00:192:168:101::12",
"config": {
"ip": "fc00:192:168:101::12",
"prefix-length": "64"
},
"state": {
"ip": "fc00:192:168:101::12",
"prefix-length": "64",
"origin": "STATIC",
"status": "PREFERRED"
}
}
},
"neighbors": {
"neighbor": [
{
"ip": "fc00:192:168:101::1",
"state": {
"ip": "fc00:192:168:101::1",
"neighbor-state": "REACHABLE",
"link-layer-address": "da:7a:6d:bf:7f:ca",
"origin": "DYNAMIC",
"is-router": null
}
},
{
"ip": "fe80::2455:a9ff:feeb:1642",
"state": {
"ip": "fe80::2455:a9ff:feeb:1642",
"neighbor-state": "REACHABLE",
"link-layer-address": "26:55:a9:eb:16:42",
"origin": "DYNAMIC",
"is-router": null
}
},
{
"ip": "fe80::d87a:6dff:febf:7fca",
"state": {
"ip": "fe80::d87a:6dff:febf:7fca",
"neighbor-state": "REACHABLE",
"link-layer-address": "da:7a:6d:bf:7f:ca",
"origin": "DYNAMIC",
"is-router": null
}
},
{
"ip": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"state": {
"ip": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"neighbor-state": "REACHABLE",
"link-layer-address": "00:00:00:00:00:00",
"origin": "STATIC"
}
}
]
}
}
}
}
},
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
{
"name": "Management1",
"config": {
"enabled": "true",
"load-interval": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "300"
},
"loopback-mode": "false",
"mtu": "0",
"name": "Management1",
"tpid": {
"@xmlns": "http://openconfig.net/yang/vlan",
"@xmlns:oc-vlan-types": "http://openconfig.net/yang/vlan-types",
"#text": "oc-vlan-types:TPID_0X8100"
},
"type": {
"@xmlns:ianaift": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "ianaift:ethernetCsmacd"
}
},
"ethernet": {
"@xmlns": "http://openconfig.net/yang/interfaces/ethernet",
"config": {
"fec-encoding": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"disabled": "false",
"fire-code": "false",
"reed-solomon": "false",
"reed-solomon544": "false"
},
"forwarding-viable": {
"@xmlns": "http://openconfig.net/yang/hercules/interfaces",
"#text": "true"
},
"mac-address": "00:00:00:00:00:00",
"port-speed": "SPEED_UNKNOWN",
"sfp-1000base-t": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "false"
}
},
"pfc": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"priorities": {
"priority": [
{
"index": "0",
"state": {
"in-frames": "0",
"index": "0",
"out-frames": "0"
}
},
{
"index": "1",
"state": {
"in-frames": "0",
"index": "1",
"out-frames": "0"
}
},
{
"index": "2",
"state": {
"in-frames": "0",
"index": "2",
"out-frames": "0"
}
},
{
"index": "3",
"state": {
"in-frames": "0",
"index": "3",
"out-frames": "0"
}
},
{
"index": "4",
"state": {
"in-frames": "0",
"index": "4",
"out-frames": "0"
}
},
{
"index": "5",
"state": {
"in-frames": "0",
"index": "5",
"out-frames": "0"
}
},
{
"index": "6",
"state": {
"in-frames": "0",
"index": "6",
"out-frames": "0"
}
},
{
"index": "7",
"state": {
"in-frames": "0",
"index": "7",
"out-frames": "0"
}
}
]
}
},
"state": {
"auto-negotiate": "true",
"counters": {
"in-crc-errors": "0",
"in-fragment-frames": "0",
"in-jabber-frames": "0",
"in-mac-control-frames": "0",
"in-mac-pause-frames": "0",
"in-oversize-frames": "0",
"out-mac-control-frames": "0",
"out-mac-pause-frames": "0"
},
"duplex-mode": "FULL",
"enable-flow-control": "false",
"forwarding-viable": {
"@xmlns": "http://openconfig.net/yang/hercules/interfaces",
"#text": "true"
},
"hw-mac-address": "26:55:a9:eb:16:42",
"mac-address": "26:55:a9:eb:16:42",
"negotiated-duplex-mode": "FULL",
"negotiated-port-speed": "SPEED_1GB",
"port-speed": "SPEED_1GB",
"supported-speeds": [
{
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "SPEED_1GB"
},
{
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "SPEED_AUTO"
}
]
}
},
"hold-time": {
"config": {
"down": "0",
"up": "0"
},
"state": {
"down": "0",
"up": "0"
}
},
"state": {
"admin-status": "UP",
"counters": {
"in-broadcast-pkts": "0",
"in-discards": "0",
"in-errors": "0",
"in-fcs-errors": "0",
"in-multicast-pkts": "0",
"in-octets": "77456281",
"in-unicast-pkts": "563249",
"out-broadcast-pkts": "0",
"out-discards": "0",
"out-errors": "0",
"out-multicast-pkts": "0",
"out-octets": "103973563",
"out-unicast-pkts": "599738"
},
"enabled": "true",
"hardware-port": {
"@xmlns": "http://openconfig.net/yang/platform/port",
"#text": "Port97"
},
"ifindex": "999001",
"inactive": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "false"
},
"last-change": "1635005976891235840",
"mtu": "0",
"name": "Management1",
"oper-status": "UP",
"tpid": {
"@xmlns": "http://openconfig.net/yang/vlan",
"@xmlns:oc-vlan-types": "http://openconfig.net/yang/vlan-types",
"#text": "oc-vlan-types:TPID_0X8100"
},
"type": {
"@xmlns:ianaift": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "ianaift:ethernetCsmacd"
}
},
"subinterfaces": {
"subinterface": {
"index": "0",
"config": {
"enabled": "true",
"index": "0"
},
"ipv4": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "192.168.101.13",
"config": {
"addr-type": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "PRIMARY"
},
"ip": "192.168.101.13",
"prefix-length": "24"
},
"state": {
"addr-type": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "PRIMARY"
},
"ip": "192.168.101.13",
"origin": "STATIC",
"prefix-length": "24"
}
}
},
"config": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
},
"neighbors": {
"neighbor": [
{
"ip": "192.168.101.1",
"config": {
"ip": "192.168.101.1"
},
"state": {
"ip": "192.168.101.1",
"link-layer-address": "da:7a:6d:bf:7f:ca",
"origin": "DYNAMIC"
}
},
{
"ip": "192.168.101.12",
"config": {
"ip": "192.168.101.12"
},
"state": {
"ip": "192.168.101.12",
"link-layer-address": "b6:cd:83:84:99:a1",
"origin": "DYNAMIC"
}
}
]
},
"state": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
},
"unnumbered": {
"config": {
"enabled": "false"
},
"state": {
"enabled": "false"
}
}
},
"ipv6": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "fc00:192:168:101::13",
"config": {
"ip": "fc00:192:168:101::13",
"prefix-length": "64"
},
"state": {
"ip": "fc00:192:168:101::13",
"origin": "STATIC",
"prefix-length": "64",
"status": "PREFERRED"
}
}
},
"config": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
},
"neighbors": {
"neighbor": [
{
"ip": "fe80::b4cd:83ff:fe84:99a1",
"config": {
"ip": "fe80::b4cd:83ff:fe84:99a1"
},
"state": {
"ip": "fe80::b4cd:83ff:fe84:99a1",
"link-layer-address": "b6:cd:83:84:99:a1",
"neighbor-state": "REACHABLE",
"origin": "DYNAMIC"
}
},
{
"ip": "fe80::5c1f:22ff:fec7:51c",
"config": {
"ip": "fe80::5c1f:22ff:fec7:51c"
},
"state": {
"ip": "fe80::5c1f:22ff:fec7:51c",
"link-layer-address": "5e:1f:22:c7:05:1c",
"neighbor-state": "REACHABLE",
"origin": "DYNAMIC"
}
}
]
},
"state": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
}
},
"state": {
"enabled": "true",
"index": "0"
}
}
}
},
{
"name": "Loopback0",
"config": {
"enabled": "true",
"load-interval": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "300"
},
"loopback-mode": "true",
"name": "Loopback0",
"tpid": {
"@xmlns": "http://openconfig.net/yang/vlan",
"@xmlns:oc-vlan-types": "http://openconfig.net/yang/vlan-types",
"#text": "oc-vlan-types:TPID_0X8100"
},
"type": {
"@xmlns:ianaift": "urn:ietf:params:xml:ns:yang:iana-if-type",
"#text": "ianaift:softwareLoopback"
}
},
"hold-time": {
"config": {
"down": "0",
"up": "0"
},
"state": {
"down": "0",
"up": "0"
}
},
"state": {
"enabled": "true",
"name": "Loopback0",
"tpid": {
"@xmlns": "http://openconfig.net/yang/vlan",
"@xmlns:oc-vlan-types": "http://openconfig.net/yang/vlan-types",
"#text": "oc-vlan-types:TPID_0X8100"
}
},
"subinterfaces": {
"subinterface": {
"index": "0",
"config": {
"enabled": "true",
"index": "0"
},
"ipv4": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"addresses": {
"address": {
"ip": "10.0.255.33",
"config": {
"addr-type": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "PRIMARY"
},
"ip": "10.0.255.33",
"prefix-length": "32"
},
"state": {
"addr-type": {
"@xmlns": "http://arista.com/yang/openconfig/interfaces/augments",
"#text": "PRIMARY"
},
"ip": "10.0.255.33",
"origin": "STATIC",
"prefix-length": "32"
}
}
},
"config": {
"dhcp-client": "false",
"enabled": "true",
"mtu": "1500"
},
"state": {
"dhcp-client": "false",
"enabled": "true",
"mtu": "1500"
},
"unnumbered": {
"config": {
"enabled": "false"
},
"state": {
"enabled": "false"
}
}
},
"ipv6": {
"@xmlns": "http://openconfig.net/yang/interfaces/ip",
"config": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
},
"state": {
"dhcp-client": "false",
"enabled": "false",
"mtu": "1500"
}
},
"state": {
"enabled": "true",
"index": "0"
}
}
}
}
]
}
}
}
}
====================================================================================
Completed in 0:00:02.034488 for eos1
====================================================================================
The output is quite large, even despite we have significantly truncated it. At the same time, you may see the level of the details available for you directly out of the box in a vendor-agnostic format:
- You can see configuration of the interfaces and the operational stated of the speed, MAC address, duplex, media type, description operation and administrative states.
- On top of that, you can see all the counters for interfaces and subinterfaces and their IP addresses.
- Finally, you see the ARP entries per each interface, where they exist.
GitHub Repository
You can find these and other scripts in our GitHub repository.
Lessons Learned
The interesting observation we made in terms of NAPLAM and Cisco IOS XR. We tried to run the script against the xr1 without pre-configuration. We’ve got the following mistake:
1 napalm.base.exceptions.ConnectionException: XML agent is not enabled. Please configure `xml agent tty iteration off`!
This was quite handy, as applying this command solved the issue. Unfortunately, there was no such a hint for Arista EOS eAPI configuration.
Build your own and your network’s stable future with our Network Automation Training.
Conclusion
Over the course of this blogpost you have seen two multivendor approach to collect and normalise the operational and configurational data from network devices running Cisco IOS XR and Arista EOS. Both of them provided results; however, the OpenConfig provided much more details compared to NAPALM. To be fair, there are other methods in NAPALM module, such as get_interfaces_counters(), which allow you to collect counters. May be those methods will satisfy your needs. At this stage we can say that OpenConfig/YANG and NAPALM completed the task in terms of the vendor-agnostic info collection. However, at the stage of configuration collection NAPALM stops being vendor-agnostic. Take care and good bye.
Support us
P.S.
If you have further questions or you need help with your networks, we are happy to assist you, just send us a message. Also don’t forget to share the article on your social media, if you like it.
BR,
Anton Karneliuk