Site icon Karneliuk

Automation 15. The Good, The Bad and the Ugly of Model-Driven Network Automation Featuring Cisco, Nokia, and OpenConfig YANG

Hello my friend,

All of us (definitely me, at least) are always thrilled hearing news from network vendors on their implementation of model-driven interfaces for network management. Having spent years automating network devices in a text-based paradigm (i.e., from CLI-based automation to full fledged configuration rendering with a replacement), I’m a firm believer that model-driven approach based on YANG modules and protocols such as GNMI, NETCONF, and RESTCONF, is a proper way to go. Recently we disclosed the development we are doing in terms of network topology visualization with DANT. And today we’d like to share lessons learned based on that experience.

We planned to write this blogpost for a few weeks if not months, but due to various reasons it was delayed. We are delighted to finally post it, so that you can get some useful ideas how you can build your own CI/CD pipeline with GitHub, probably the most popular platform for collaborative software development.


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.

Does Network Automation Help with Troubleshooting?

One of the aspects of network automation is to collect the data from network devices in a reliable and structured manner. The structure is coming from YANG modules, and the reliability from the protocols, which are implemented in the network functions. As such understanding of YANG, NETCONF, RESTCONF, and GNMI are essential to be able to quickly retrieve and analyze the relevant data to equip you with proper tools for the present and future of networking

And in our Network Automation Trainings you will learn about NETCONF, GNMI, RESTCONF with YANG modules on a protocol level and how to use them with Ansible and Python, in addition to many other things.

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

So what are YANG modules in a nutshell? And why are they so important in context of them model-driven network automation (from now on we will be simply calling it the network automation omitting “model-driven”)?

To put it simple, a YANG module contains information about all possible pieces of information, which you can:

There is a number (sometimes this number is very big) of YANG modules per each network operating system. As such, altogether all the YANG modules collectively describe everything you have inside your network device: every single counter or other data available in your device.

Here is a quick snippet from YANG modules for Cisco NX OS (network operating system running on Cisco Nexus devices), which gives you impression how the data within the YANG module can be structured:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module: Cisco-NX-OS-device
  +--rw System
     +--rw name?                       string
     +--ro currentTime?                union
     +--ro systemUpTime?               union
     +--ro serial?                     eqpt_Serial
     +--ro configModTs?                mo_TStamp
     +--ro configDirty?                boolean
     +--ro configModTsBackup?          mo_TStamp
     +--ro configDirtyBackup?          boolean
     +--rw bgp-items
     |  +--rw name?         naming_Name256
     |  +--rw adminSt?      nw_AdminSt
     |  +--ro operSt?       nw_EntOperSt
     |  +--rw inst-items
     |     +--rw asn?                   bgp_AsnNum
     |     +--rw disPolBatch?           bgp_AdminSt
     |     +--rw disPolBatchv4PfxLst?   string
     |     +--rw disPolBatchv6PfxLst?   string
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY

Enroll to Zero-to-Hero Network Automation Training to learn how to deal with YANG modules and even create your own.

Benefits of YANG-based Automation

Among the benefits of the model-driven approach to network automation (based on YANG and NETCONF or GNMI) the following are very important:

Challenges of YANG-based Automation

There are two main from our experience:

Solution for YANG-based Automation

There is a solution for the last point, though. It is called OpenConfig, which is a collection of YANG modules, which is already supported by a number of network vendors. However, the implementation in many cases is, unfortunately, not flawless. The main problems are:

In our Zero-to-Hero Network Automation Trainings we in-depth explore OpenConfig YANG and you practice a lot how to automate Cisco, Nokia, and Arista with NETCONF/YANG and GNMI/YANG.

Let’s take a look into the real practical case we have worked on recently.

YANG Modules in Action

Our use case was to visualize the multi vendor network topology running Cisco IOS XR and Nokia SR OS. The network running quite recent SW versions:

The latest versions give us possibility to use the model driven automation without any issues. So the goal we have formulated as the following:

Data Collection and Visualization

We won’t spend a lot of time in description of this part just sharing highlights:

Enroll to Zero-to-Hero Network Automation Training to learn how to use NETCONF, NetworkX and pyvis in Python.

Enroll to Network Automation with Nornir to master skills of using Nornir framework.

The main challenge though was not collect data from network devices, but rather to analyze it to be able to build a graph. Let’s take a look how different vendors store data about ISIS configuration in native YANG modules and then let’s compare it with OpenConfig YANG modules.

Cisco IOS XR Implementation of YANG modules with ISIS Operational Data

Cisco IOS XR has very segmented sturcutre of YANG modules, i.e. there is no single top-level module. After quick look, we located the module named “Cisco-IOS-XR-clns-isis-oper.yang“, which contains operational information about ISIS. The amount of information is just huge, starting from the general information such as system-id, area ID, and device level to the content of the ISIS LSDB. From our task perspective, the most interesting part is the one, which contains interfaces and adjacencies info:


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
$ pyang -f tree -p yang/vendor/cisco/xr/721/. yang/vendor/cisco/xr/721/Cisco-IOS-XR-clns-isis-oper.yang
module: Cisco-IOS-XR-clns-isis-oper
  +--ro isis
     +--ro instances
     |  +--ro instance* [instance-name]
     |     +--ro neighbors
     |     |  +--ro neighbor* []
     |     |     +--ro system-id?                          xr:Osi-system-id
     |     |     +--ro interface-name?                     xr:Interface-name
     |     |     +--ro neighbor-system-id?                 xr:Osi-system-id
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     |     +--ro interfaces
     |     |  +--ro interface* [interface-name]
     |     |     +--ro interface-name               xr:Interface-name
     |     |     +--ro configured-status
     |     |     |  +--ro adjacency-form-status?   boolean
     |     |     |  +--ro adv-prefix-status?       boolean
     |     |     +--ro interface-status-and-data
     |     |     |  +--ro disabled
     |     |     |  |  +--ro reason-code?   Isis-if-disabled-reason
     |     |     |  +--ro enabled
     |     |     |  |  +--ro adjacency-form-status
     |     |     |  |  |  +--ro disabled
     |     |     |  |  |  |  +--ro reason-code?   Isis-if-adj-form-disabled-reason
     |     |     |  |  |  +--ro status?     Isis-enabled
     |     |     |  |  +--ro adv-prefix-status
     |     |     |  |  |  +--ro disabled
     |     |     |  |  |  |  +--ro reason-code?   Isis-if-adv-prefix-disabled-reason
     |     |     |  |  |  +--ro status?     Isis-enabled
     |     |     |  |  +--ro p2p-data
     |     |     |  |  |  +--ro time-until-next-iih?         uint32
     |     |     |  |  |  +--ro retransmit-lsp-queue-size?   uint32
     |     |     |  |  |  +--ro retransmit-lsp-interval?     uint32
     |     |     |  |  |  +--ro extended-circuit-number?     Isis-extended-circuit-number
     |     |     |  |  +--ro bfd-data
     |     |     |  |  |  +--ro enabled?        boolean
     |     |     |  |  |  +--ro ipv6-enabled?   boolean
     |     |     |  |  |  +--ro interval?       uint32
     |     |     |  |  |  +--ro multiplier?     uint32
     |     |     |  |  +--ro clns-data
     |     |     |  |  |  +--ro clns-status
     |     |     |  |  |  |  +--ro clns-down-info
     |     |     |  |  |  |  |  +--ro reason-code?   Isis-if-clns-proto-down-reason
     |     |     |  |  |  |  +--ro status?           Isis-up
     |     |     |  |  |  +--ro mtu-info
     |     |     |  |  |  |  +--ro invalid
     |     |     |  |  |  |  |  +--ro reason-code?   Isis-if-clns-mtu-invalid-reason
     |     |     |  |  |  |  +--ro status?    Isis-valid
     |     |     |  |  |  |  +--ro mtu?       uint32
     |     |     |  |  |  +--ro snpa-state
     |     |     |  |  |  |  +--ro unknown
     |     |     |  |  |  |  |  +--ro reason-code?   Isis-if-clns-snpa-unknown-reason
     |     |     |  |  |  |  +--ro known
     |     |     |  |  |  |  |  +--ro snpa?   xr:Isis-snpa
     |     |     |  |  |  |  +--ro status?    Isis-known
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     |     |     +--ro adjacencies
     |     |     |  +--ro adjacency* []
     |     |     |     +--ro system-id?                           xr:Osi-system-id
     |     |     |     +--ro interface-name?                      xr:Interface-name
     |     |     |     +--ro adjacency-system-id?                 xr:Osi-system-id
     |     |     |     +--ro adjacency-snpa?                      xr:Isis-snpa
     |     |     |     +--ro adjacency-interface?                 xr:Interface-name
     |     |     |     +--ro adjacency-media-type?                Isis-media-class
     |     |     |     +--ro adjacency-state?                     Isis-adj-state
     |     |     |     +--ro adjacency-bfd-state?                 Isis-adj-bfd-state
     |     |     |     +--ro adjacency-ipv6bfd-state?             Isis-adj-bfd-state
     |     |     |     +--ro adj-ipv4bfd-retry-running?           boolean
     |     |     |     +--ro adj-ipv6bfd-retry-running?           boolean
     |     |     |     +--ro adj-ipv4bfd-retry-exp?               uint32
     |     |     |     +--ro adj-ipv6bfd-retry-exp?               uint32
     |     |     |     +--ro adj-ipv4bfd-retry-count?             uint32
     |     |     |     +--ro adj-ipv6bfd-retry-count?             uint32
     |     |     |     +--ro adjacency-uptime-valid-flag?         boolean
     |     |     |     +--ro adjacency-uptime?                    uint32
     |     |     |     +--ro adjacency-holdtime?                  uint32
     |     |     |     +--ro adjacency-checkpoint-object-id?      uint32
     |     |     |     +--ro adjacency-ietf-nsf-capable-flag?     boolean
     |     |     |     +--ro adjacency-dispriority?               Isis-dr-priority
     |     |     |     +--ro adjacency-neighbor-priority?         Isis-dr-priority
     |     |     |     +--ro adjacency-local-priority?            Isis-dr-priority
     |     |     |     +--ro local-dis-flag?                      boolean
     |     |     |     +--ro neighbor-dis-flag?                   boolean
     |     |     |     +--ro nsr-standby?                         uint8
     |     |     |     +--ro bfd-required?                        boolean
     |     |     |     +--ro neighbor-useable?                    boolean
     |     |     |     +--ro bfd-change-pending?                  boolean
     |     |     |     +--ro adjacency-area-address* []
!
! FUTHER OUTPUT IS TRUNCATED FOR BREVITY

If the one carefully analyses the provided output above, it is possible to retrieve the critical information there:

By matching all these three parameters it is possible to unambiguously build the network graph for ISIS adjacency for Cisco IOS XR based network functions.

You can find Cisco IOS XR YANG modules in this GitHub repo.

Nokia SR OS Implementation of YANG modules with ISIS Operational Data

Let’s take a look how Nokia SR OS approach storing of the same information. In contrast to Cisco IOS XR, Nokia SR OS has a single top-level YANG module for operational data called “nokia-state.yang“, which is ultimately calling all other sub-modules, when it is needed. As its content is astonishingly long, we will show only a few tiny pieces related to the same task:


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
$ pyang -f tree -p yang/vendor/nokia/7x50_YangModels/latest_sros_22.5/. yang/vendor/nokia/7x50_YangModels/latest_sros_22.5/nokia-submodule/nokia-state.yang
module: nokia-state
  +--ro state
     +--ro aaa
     |  +--ro radius
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     +--ro router* [router-name]
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     |  +--ro isis* [isis-instance]
     |  |  +--ro isis-instance                      int32
     |  |  +--ro oper-system-id?                    types-igp:system-id
     |  |  +--ro oper-router-id?                    types-sros:ipv4-address
     |  |  +--ro oper-ipv6-te-router-id?            types-sros:ipv6-address
     |  |  +--ro db-export-igp-asn?                 inet:as-number
     |  |  +--ro oper-state?                        types-sros:oper-state
     |  |  +--ro last-enabled-time?                 yang:date-and-time
     |  |  +--ro last-spf?                          yang:date-and-time
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     |  |  +--ro interface* [interface-name]
     |  |  |  +--ro interface-name               types-sros:interface-name
     |  |  |  +--ro oper-state?                  types-sros:oper-state
     |  |  |  +--ro mtu?                         uint32
     |  |  |  +--ro circuit-index?               uint32
     |  |  |  +--ro oper-type?                   types-isis:interface-type
     |  |  |  +--ro ber-state?                   enumeration
     |  |  |  +--ro te-metric?                   types-isis:oper-metric
     |  |  |  +--ro te-state?                    types-sros:oper-state
     |  |  |  +--ro point-to-point-circuit-id?   types-isis:circuit-id
     |  |  |  +--ro up-time?                     int32
     |  |  |  +--ro statistics
     |  |  |  |  +--ro initialization-failures?                   yang:counter32
     |  |  |  |  +--ro adjacency-state-change?                    yang:counter32
     |  |  |  |  +--ro adjacency-rejection?                       yang:counter32
     |  |  |  |  +--ro control-pdu-sent?                          yang:counter32
     |  |  |  |  +--ro control-pdu-received?                      yang:counter32
     |  |  |  |  +--ro control-pdu-id-length-mismatch-received?   yang:counter32
     |  |  |  |  +--ro max-area-address-mismatches?               yang:counter32
     |  |  |  |  +--ro area-address-mismatches?                   yang:counter32
     |  |  |  |  +--ro auth-type-fails?                           yang:counter32
     |  |  |  |  +--ro auth-fails?                                yang:counter32
     |  |  |  |  +--ro lan-dis-changes?                           yang:counter32
     |  |  |  |  +--ro end-x-install-failures?                    yang:counter32
     |  |  |  |  +--ro adj-number?                                uint32
     |  |  |  +--ro ldp-sync
     |  |  |  |  +--ro state?         types-sros:oper-state
     |  |  |  |  +--ro max-metric?    boolean
     |  |  |  |  +--ro timer-state?   enumeration
     |  |  |  |  +--ro time-left?     uint32
     |  |  |  +--ro ipv4-shared-sid
     |  |  |  |  +--ro (type)?
     |  |  |  |     +--:(label)
     |  |  |  |     |  +--ro label?   int64
     |  |  |  |     +--:(index)
     |  |  |  |        +--ro index?   int64
     |  |  |  +--ro ipv6-shared-sid
     |  |  |  |  +--ro (type)?
     |  |  |  |     +--:(label)
     |  |  |  |     |  +--ro label?   int64
     |  |  |  |     +--:(index)
     |  |  |  |        +--ro index?   int64
     |  |  |  +--ro level* [level-number]
     |  |  |  |  +--ro level-number                      enumeration
     |  |  |  |  +--ro adjacencies-number?               uint32
     |  |  |  |  +--ro designated-intermediate-system?   types-igp:system-id
     |  |  |  |  +--ro lsp-transmit-queue-count?         uint32
     |  |  |  |  +--ro circuit-id?                       types-isis:circuit-id
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
     |  |  |  +--ro adjacency* [adjacency-index]
     |  |  |  |  +--ro adjacency-index       int32
     |  |  |  |  +--ro oper-state?           enumeration
     |  |  |  |  +--ro usage?                enumeration
     |  |  |  |  +--ro hold-timer?           int32
     |  |  |  |  +--ro uptime?               int32
     |  |  |  |  +--ro expires-in?           int32
     |  |  |  |  +--ro level?                enumeration
     |  |  |  |  +--ro restart
     |  |  |  |  |  +--ro support?        boolean
     |  |  |  |  |  +--ro status?         enumeration
     |  |  |  |  |  +--ro suppressed?     boolean
     |  |  |  |  |  +--ro num-restarts?   uint32
     |  |  |  |  |  +--ro last-restart?   yang:date-and-time
     |  |  |  |  +--ro neighbor
     |  |  |  |  |  +--ro snpa-address?   types-isis:snpa-address
     |  |  |  |  |  +--ro system-type?    enumeration
     |  |  |  |  |  +--ro system-id?      string
     |  |  |  |  |  +--ro priority?       int32
     |  |  |  |  |  +--ro ipv4?           types-sros:ipv4-address
     |  |  |  |  |  +--ro ipv6?           types-sros:ipv6-address
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY

When we start analysing the sturcutre of YANG modules, there are different between Nokia SR OS and Cisco IOS XR, even in such a small use cases as ISIS adjacency:

We won’t argue what is better: as long as we can retrieve information we need, we could be fine.

Obviously, we would need to build our code in the corresponding way, so that both Cisco IOS XR and Nokia SR OS output can be analyzed.

However, there is one complication with Nokia SR OS modules:

But the own SNPA of the intreface is missing in the ISIS subtree of Nokia native YANG modules.

So, approach we took in DANT is to:

So it is still possible to do the same job in Nokia SR OS, but requires more info processing.

OpenConfig Implementation of YANG modules with ISIS Operational Data

Finally, let’s take a look how it is working in OpenConfig YANG modules. Like Cisco IOS XR it has a segmented structure, with different YANG modules being responsible for different parts of confguration. The module covering the routing configuration is named “openconfig-network-instaces.yang“, and it includes ISIS as well. As ususal, only the relevant part is shown:


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
$ pyang -f tree -p public/release/models/ public/release/models/network-instance/openconfig-network-instance.yang
module: openconfig-network-instance
  +--rw network-instances
     +--rw network-instance* [name]
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
        +--rw protocols
           +--rw protocol* [identifier name]
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
              +--isis*

!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
              |  +--rw interfaces
              |     +--rw interface* [interface-id]
              |        +--rw interface-id        -> ../config/interface-id
              |        +--rw config
              |        |  +--rw enabled?         boolean
              |        |  +--rw interface-id?    oc-if:interface-id
              |        |  +--rw passive?         boolean
              |        |  +--rw hello-padding?   oc-isis-types:hello-padding-type
              |        |  +--rw circuit-type?    oc-isis-types:circuit-type
              |        +--ro state
              |        |  +--ro enabled?         boolean
              |        |  +--ro interface-id?    oc-if:interface-id
              |        |  +--ro passive?         boolean
              |        |  +--ro hello-padding?   oc-isis-types:hello-padding-type
              |        |  +--ro circuit-type?    oc-isis-types:circuit-type

!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
              |        +--rw levels
              |        |  +--rw level* [level-number]
              |        |     +--rw level-number            -> ../config/level-number
              |        |     +--rw config
              |        |     |  +--rw level-number?   oc-isis-types:level-number
              |        |     |  +--rw passive?        boolean
              |        |     |  +--rw priority?       uint8
              |        |     |  +--rw enabled?        boolean
              |        |     +--ro state
              |        |     |  +--ro level-number?   oc-isis-types:level-number
              |        |     |  +--ro passive?        boolean
              |        |     |  +--ro priority?       uint8
 
!
! SOME OUTPUT IS TRUNCATED FOR BREVITY
!
              |        |     +--ro adjacencies
              |        |     |  +--ro adjacency* [system-id]
              |        |     |     +--ro system-id    -> ../state/system-id
              |        |     |     +--ro state
              |        |     |        +--ro system-id?                      oc-isis-types:system-id
              |        |     |        +--ro neighbor-ipv4-address?          oc-inet:ipv4-address
              |        |     |        +--ro neighbor-ipv6-address?          oc-inet:ipv6-address
              |        |     |        +--ro neighbor-snpa?                  oc-isis-types:snpa
              |        |     |        +--ro local-extended-circuit-id?      oc-isis-types:extended-circuit-id
              |        |     |        +--ro neighbor-extended-circuit-id?   oc-isis-types:extended-circuit-id
              |        |     |        +--ro priority?                       uint8
              |        |     |        +--ro dis-system-id?                  oc-isis-types:system-id
              |        |     |        +--ro neighbor-circuit-type?          oc-isis-types:level-type
              |        |     |        +--ro adjacency-type?                 oc-isis-types:level-type
              |        |     |        +--ro adjacency-state?                oc-isis-types:isis-interface-adj-state
              |        |     |        +--ro up-timestamp?                   oc-types:timeticks64
              |        |     |        +--ro multi-topology?                 boolean
              |        |     |        +--ro topology*                       identityref
              |        |     |        +--ro restart-support?                boolean
              |        |     |        +--ro restart-suppress?               boolean
              |        |     |        +--ro restart-status?                 boolean
              |        |     |        +--ro area-address*                   oc-isis-types:area-address
              |        |     |        +--ro nlpid*                          enumeration
!
! FURTHER OUTPUT IS TRUNCATED FOR BREVITY

Yet again you can see that structure is slightly different to two previous vendors. Besides segmented vs consolidated YANG modules, another difference here is that adjacency is put inside interfaces (like in Nokia SR OS); however it is nested furtehr, as it is put inside levels/level XPath of the interface.

Our GitHub Repository

Example of Python tool, which is visualising the topology network topology leveraging YANG modules is in our GitHub.

Lessons Learned

We spent a lot of time on trying to create a properl logic for visualuation of the collected date. The main challenge is that each device shall be building visualisation explicitly from its own standpoint (i.e., Nokia SR OS based device shall be able to build a graph based solely on what is available in its YANG modules). Once such logic was implemented, it allowed us to easily build multi vendor network topoligies, connecting nodes with normalised data, offloading the vendor-specific complexity to a lelvel of initial data processing.

Summary

Network Automation with YANG modules and NETCONF and GNMI may look more complicated at a glance than a CLI based automation. It maybe the case; however, once you understand the principles, you will use it everywhere, where you can, because it makes both configuration of network devices and collecting of the information much more straightforward. Especially, if you find a way to leverage OpenConfig and other Open source YANG modules. 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