Site icon Karneliuk

Automation 20. GNMI to Spreadsheet via Pandas or Intro to Data Analysis in Network Automation

Dear friend,

We all like spreadsheets, as this is an easy and intuitive way of representing data for us, humans. The possibility to analyze data with spreadsheets are endless, starting from simple aggregation till complicated pivots are endless. Even the people who say that they don’t like spreadsheets, use them so often. Network automation, for sure, is not an exclusion here.


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.

Are Spreadsheets Valid In Automation Era?

They absolutely are. We haven’t seen so far any non-IT person, or even IT high-profile engineer or manager, which won’t like spreadsheets. To put it simple, spreadsheets can be an easy way to represent data collected from network devices; likewise, it can be a good way to provide some input, either as inventory or as configuration details for your network automation intent. So the questions becomes, provided you can read data from your spreadsheet in a programmatic way, what would you do with it?

The good news is that we can teach you all the essential and advanced concepts in Network Automation so that you can unleash the true power of network programmability with spreadsheets (and not only):

We offer the following training programs for you:

During these trainings you will learn the following topics:

Moreover, we put all mentions technologies in the context of 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 labs to master your skills thoroughly. Such a mixture creates a unique learning environment, which all students value so much. Join us and unleash your potential.

Start your automation training today.

Brief Description

We already have said enough today about why spreadsheets are still important and why we create this blogpost. From now on, we’ll focus on how we will do that.

The use case we are to look into is the one that network engineers face almost daily: collect in one place information from multiple data sources. This one place ultimately will be a spreadsheet with multiple sheets.

The way you collect information doesn’t really matter: it can be as legacy as scrapping CLI and parsing the output with regular expressions; or it can be also more future-proof, such as using the NETCONF/YANG or GNMI/YANG. In this blogpot we will use the latter. In fact, we will continue the topic we started the last week about automation of Microsoft Azure OCP SONiC with GNMI. So, let’s put the formal objectives:

Lab Setup

We are going to re-use the same topology we have used in the previous blogpost:

It is not mandatory to use SONiC. It just happened we have it already. You are welcome to use Nokia SR OS, Arista EOS, Cisco IOS XR/NX-OS, or any other OS. By the way, you can learn how to automate those platforms in our Zero-to-Hero Network Automation Training.

Let’s a bit remind what we run where:

Enroll to our Network Automation Training to learn how GNMI works and how to use it.

Solution with Python

Our solution will consist of 3 steps:

In order to be able to make these steps, we’d need the following Python libraries to be installed:

Do it with on your Linux host:


1
$ pip install pygnmi pandas openpyxl

Step 1. Collect Data via GNMI from SONiC Network Device

For this step we need installed pygnmi library

To collect the data from the network device we are going to use the pyGNMI. If we would run the script we have created in the previous blogpost, we’d see that there is a number of YANG modules supported by SONiC:


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
$ python sonic_test_pygnmi.py
{
    "supported_models": [
        {
            "name": "openconfig-acl",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "openconfig-acl",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "openconfig-interfaces",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "openconfig-lldp",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "openconfig-platform",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "openconfig-system",
            "organization": "OpenConfig working group",
            "version": "1.0.2"
        },
        {
            "name": "ietf-yang-library",
            "organization": "IETF NETCONF (Network Configuration) Working Group",
            "version": "2016-06-21"
        },
        {
            "name": "sonic-db",
            "organization": "SONiC",
            "version": "0.1.0"
        }
    ],
    "supported_encodings": [
        "json",
        "json_ietf"
    ],
    "gnmi_version": "0.7.0"
}

Enroll to our Network Automation Trainings to learn more about YANG and OpenConfig.

Initially we started with OpenConfig/YANG modules. However, either due to the fact we are using the VM or due to some other fact, we were unable to retrieve any data. Probably, we should have re-build the SONiC image; however, as it (how OpenConfig works on Microsoft Azure OCP SONiC) is not the core focus of this blogpost, we decided to look what else we can retrieve. Looking in the output above you can spot that there is some sonic-db YANG module. Looking further, we figured out there is a small number of SONiC-specific YANG modules, which can be used as well. So we tried to collect information from those modules and we actually were able to get something:


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
$ cat sonic_gnmi_to_spreadsheet.py
# Modules
from pygnmi.client import gNMIclient
import json


# Variables
TARGET = {
    "host": "dev-pygnmi-sonic-003",
    "port": 50051,
    "username": "admin",
    "password": "YourPaSsWoRd",
}


# Body
if __name__ == "__main__":
    with gNMIclient(
        target=(TARGET["host"], TARGET["port"]),
        username=TARGET["username"],
        password=TARGET["password"],
        path_root="certs/ca.pem",
        path_cert="certs/server.pem",
        path_key="certs/server.key",
    ) as gc:
        path = ["/sonic-interface:sonic-interface/INTERFACE"]
        result2 = gc.get(path=path, encoding="json_ietf")
        print(json.dumps(result2, indent=4))

Check pyGNMI blogpost to get the basics of pygnmi.

Let’s execute this script:


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
$ python sonic_gnmi_to_spreadsheet.py
{
    "notification": [
        {
            "timestamp": 1673211099162493295,
            "prefix": null,
            "alias": null,
            "atomic": false,
            "update": [
                {
                    "path": "sonic-interface/INTERFACE",
                    "val": {
                        "sonic-interface:INTERFACE": {
                            "INTERFACE_IPADDR_LIST": [
                                {
                                    "ip_prefix": "10.0.0.0/31",
                                    "portname": "Ethernet0"
                                },
                                {
                                    "ip_prefix": "10.0.0.50/31",
                                    "portname": "Ethernet100"
                                },
                                {
                                    "ip_prefix": "10.0.0.52/31",
                                    "portname": "Ethernet104"
                                },
                                {
                                    "ip_prefix": "10.0.0.54/31",
                                    "portname": "Ethernet108"
                                },
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY

Alright, as we don’t have too much information, but we have SOME, which means we can start building our spreadsheet.

Step 2. Build Pandas Data Frame

For this step we need pandas library

Following the official documentation, Data Frame is a 2-dimensional informational instance. 2 dimensions means, that it is, in a nutshell, table or matrix, where we have different tables and rows and we may provide names for either one of dimension (say, only columns or only rows) or to both of them. There are multiple ways how you can provide names for rows (in Pandas it is called “indexes“) or columns (unsurprisingly, they are also called “columns” in Pandas). In our case, as we create Pandas Data Frame from a Python dictionary (to be precise, from the list of identical dictionaries), we can use the name of keys from dictionaries to automatically generate the names of columns for us. Let’s amend our code to create and visualize data frames for us:


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
# Modules
from pygnmi.client import gNMIclient
import pandas


# Variables
TARGET = {
    "host": "dev-pygnmi-sonic-003",
    "port": 50051,
    "username": "admin",
    "password": "YourPaSsWoRd",
}


# Body
if __name__ == "__main__":
    with gNMIclient(
        target=(TARGET["host"], TARGET["port"]),
        username=TARGET["username"],
        password=TARGET["password"],
        path_root="certs/ca.pem",
        path_cert="certs/server.pem",
        path_key="certs/server.key",
    ) as gc:
        path = ["/sonic-interface:sonic-interface/INTERFACE"]
        result2 = gc.get(path=path, encoding="json_ietf")

        for path_data in result2["notification"][0]["update"]:
            for path_data_key, path_data_val in path_data["val"].items():
                sheets = {}

                for entry_key, entry_val in path_data_val.items():
                    target_data = [{"hostname": TARGET["host"], **entry} for entry in entry_val]
                    sheets[entry_key] = pandas.DataFrame(target_data)

                    print(entry_key)
                    print(sheets[entry_key])

Let’s quickly review, what has changed:

Here what we have as a result of the execution:


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
$ python sonic_gnmi_to_spreadsheet.py
INTERFACE_IPADDR_LIST
                hostname     ip_prefix     portname
0   dev-pygnmi-sonic-003   10.0.0.0/31    Ethernet0
1   dev-pygnmi-sonic-003  10.0.0.50/31  Ethernet100
2   dev-pygnmi-sonic-003  10.0.0.52/31  Ethernet104
3   dev-pygnmi-sonic-003  10.0.0.54/31  Ethernet108
4   dev-pygnmi-sonic-003  10.0.0.56/31  Ethernet112
5   dev-pygnmi-sonic-003  10.0.0.58/31  Ethernet116
6   dev-pygnmi-sonic-003   10.0.0.6/31   Ethernet12
7   dev-pygnmi-sonic-003  10.0.0.60/31  Ethernet120
8   dev-pygnmi-sonic-003  10.0.0.62/31  Ethernet124
9   dev-pygnmi-sonic-003   10.0.0.8/31   Ethernet16
10  dev-pygnmi-sonic-003  10.0.0.10/31   Ethernet20
11  dev-pygnmi-sonic-003  10.0.0.12/31   Ethernet24
12  dev-pygnmi-sonic-003  10.0.0.14/31   Ethernet28
13  dev-pygnmi-sonic-003  10.0.0.16/31   Ethernet32
14  dev-pygnmi-sonic-003  10.0.0.18/31   Ethernet36
15  dev-pygnmi-sonic-003   10.0.0.2/31    Ethernet4
16  dev-pygnmi-sonic-003  10.0.0.20/31   Ethernet40
17  dev-pygnmi-sonic-003  10.0.0.22/31   Ethernet44
18  dev-pygnmi-sonic-003  10.0.0.24/31   Ethernet48
19  dev-pygnmi-sonic-003  10.0.0.26/31   Ethernet52
20  dev-pygnmi-sonic-003  10.0.0.28/31   Ethernet56
21  dev-pygnmi-sonic-003  10.0.0.30/31   Ethernet60
22  dev-pygnmi-sonic-003  10.0.0.32/31   Ethernet64
23  dev-pygnmi-sonic-003  10.0.0.34/31   Ethernet68
24  dev-pygnmi-sonic-003  10.0.0.36/31   Ethernet72
25  dev-pygnmi-sonic-003  10.0.0.38/31   Ethernet76
26  dev-pygnmi-sonic-003   10.0.0.4/31    Ethernet8
27  dev-pygnmi-sonic-003  10.0.0.40/31   Ethernet80
28  dev-pygnmi-sonic-003  10.0.0.42/31   Ethernet84
29  dev-pygnmi-sonic-003  10.0.0.44/31   Ethernet88
30  dev-pygnmi-sonic-003  10.0.0.46/31   Ethernet92
31  dev-pygnmi-sonic-003  10.0.0.48/31   Ethernet96
INTERFACE_LIST
                hostname     portname
0   dev-pygnmi-sonic-003    Ethernet0
1   dev-pygnmi-sonic-003  Ethernet100
2   dev-pygnmi-sonic-003  Ethernet104
3   dev-pygnmi-sonic-003  Ethernet108
4   dev-pygnmi-sonic-003  Ethernet112
5   dev-pygnmi-sonic-003  Ethernet116
6   dev-pygnmi-sonic-003   Ethernet12
7   dev-pygnmi-sonic-003  Ethernet120
8   dev-pygnmi-sonic-003  Ethernet124
9   dev-pygnmi-sonic-003   Ethernet16
10  dev-pygnmi-sonic-003   Ethernet20
11  dev-pygnmi-sonic-003   Ethernet24
12  dev-pygnmi-sonic-003   Ethernet28
13  dev-pygnmi-sonic-003   Ethernet32
14  dev-pygnmi-sonic-003   Ethernet36
15  dev-pygnmi-sonic-003    Ethernet4
16  dev-pygnmi-sonic-003   Ethernet40
17  dev-pygnmi-sonic-003   Ethernet44
18  dev-pygnmi-sonic-003   Ethernet48
19  dev-pygnmi-sonic-003   Ethernet52
20  dev-pygnmi-sonic-003   Ethernet56
21  dev-pygnmi-sonic-003   Ethernet60
22  dev-pygnmi-sonic-003   Ethernet64
23  dev-pygnmi-sonic-003   Ethernet68
24  dev-pygnmi-sonic-003   Ethernet72
25  dev-pygnmi-sonic-003   Ethernet76
26  dev-pygnmi-sonic-003    Ethernet8
27  dev-pygnmi-sonic-003   Ethernet80
28  dev-pygnmi-sonic-003   Ethernet84
29  dev-pygnmi-sonic-003   Ethernet88
30  dev-pygnmi-sonic-003   Ethernet92
31  dev-pygnmi-sonic-003   Ethernet96

Despite those 2 Pandas Data Frames are very close to each other, they are coming from different branches in the SONiC YANG module for interfaces. As such, if we happen to collect the data from real network devices, the information may be much more rich.

Step 3. Save Data Frame to Spreadsheet

For this step we need openpyxl library.

The last step in our journey is to write the created data frame into the spreadsheet. To do that, we need to further amend our code.


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
# Modules
import os
from pygnmi.client import gNMIclient
import pandas


# Variables
OUTPUT_DIR = "./output"
TARGET = {
    "host": "dev-pygnmi-sonic-003",
    "port": 50051,
    "username": "admin",
    "password": "YourPaSsWoRd",
}


# Body
if __name__ == "__main__":
    with gNMIclient(
        target=(TARGET["host"], TARGET["port"]),
        username=TARGET["username"],
        password=TARGET["password"],
        path_root="certs/ca.pem",
        path_cert="certs/server.pem",
        path_key="certs/server.key",
    ) as gc:
        path = ["/sonic-interface:sonic-interface/INTERFACE"]
        result2 = gc.get(path=path, encoding="json_ietf")

        for path_data in result2["notification"][0]["update"]:
            for path_data_key, path_data_val in path_data["val"].items():
                if not os.path.exists(OUTPUT_DIR):
                    os.mkdir(OUTPUT_DIR)

                spreadsheet = pandas.ExcelWriter(path=f"{OUTPUT_DIR}/{path_data_key}.xlsx")
                sheets = {}

                for entry_key, entry_val in path_data_val.items():
                    target_data = [{"hostname": TARGET["host"], **entry} for entry in entry_val]
                    sheets[entry_key] = pandas.DataFrame(target_data)

                    print(entry_key)
                    print(sheets[entry_key])

                    sheets[entry_key].to_excel(spreadsheet, sheet_name=entry_key)

                spreadsheet.close()

Here is the breakdown of the changes:

Let’s run the script one more time:


1
2
3
4
5
6
$ python sonic_gnmi_to_spreadsheet.py
INTERFACE_IPADDR_LIST
                hostname     ip_prefix     portname
0   dev-pygnmi-sonic-003   10.0.0.0/31    Ethernet0
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY

Once the execution is completed you will see the new directory created with a file inside:


1
2
3
4
5
$ tree output
output
`-- sonic-interface:INTERFACE.xlsx

0 directories, 1 file

Let’s open the file with spreadsheet processor you have:

GitHub Repository

Check out the examples from this blogpost in our repository.

Lessons Learned

Actually, when we started writing this blogpost, we thought only about showing you how to easily retrieve the data from the network devices using GNMI and convert that data to a table and save as a spreadsheet. However, we realized that we can do much more that that. So we decided to dig into the topic of the data analysis applied for network automation much deeper and to write a few more blog posts about it.

Summary

We have not yet even scratched the surface of the data analysis with Pandas; however, we did an important step into that direction. Namely, we linked the collection of data from network devices using the model-driven protocol such as GNMI to creation and population of Pandas data frames. As a side-product, we created a multi sheet Excel spreadsheet with the data. We’ll continue on this topic. Take care and good bye!

Need Help? Contract Us

If you need a trusted and experienced partner to automate your network and IT infrastructure, get in touch with 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

Exit mobile version