Nokia SROS MD-CLI Annotations and Configuration Groups

For Nokia 7×50 Platforms (7250 IXR, 7450 ESS, 7750 SR and 7950 XRS) Nokia SROS Model Driven CLI (MD-CLI) is the modern CLI used for these platforms. The original “Classic” CLI is still available, however it is no longer enabled by default starting with SROS 23.7.R1 and would need to be reconfigured to use that interface.

MD-CLI has a number of advantages over Classic CLI:

  • Firstly as the name suggests – the interface is tightly coupled with the system configuration and operational states of the platforms it runs in – the models are reachable using NetConf and GRPc (SNMP is supported in some limited forms but is definitely not a first class citizen here – if SNMP is your bag, you will need to stay with Classic CLI, or enable Mixed-Mode management which is a hybrid of the two.
  • Transaction based configs are actively used now, in Classic CLI these were optional and often not used
  • Leverages Configuration Groups (which is one of the topics of discussion in this blog post) – this concept is not unique to Nokia and some vendors have had this concept for a really long time however it is great to see that it exists
  • Supports automation friendly actions – you can view config and state information in the same way your automation tools might use using JSON or XML and displaying things in xpath format – this is also described on the Nokia Network Developer Portal using tools like gNMIc
  • Advanced capabilities through PySROS enabling cool things like adding tools on the router to enable the creation of new CLI commands, or for advanced automation/reconfiguration actions based on system events

The biggest disadvantage is that MD-CLI is not available on the smaller platforms (7705 SAR and 7210 SAS) and for those at least at the moment Classic CLI is all that is available.

Annotations (configuration comments)

The annotation (configuration comment) feature which was introduced in SROS 21.5.R1. This capability enables the insertion of a comment within the router configuration at a particular configuration section, this might be helpful to help document why something is done in a particular manner, or to mention that something had been changed on purpose – and might stop someone at 2:00am working on a fault and trying to revert/remove an intended config.

Let me give an example – this is a configuration section of a router that we are going to change the system address and would like to annotate what happened and when.

A:admin@R1# info
    ipv4 {
        primary {
            address 192.168.1.1
            prefix-length 32
        }
    }

[gl:/configure router "Base" interface "system"]

We are going to change the IP Address and include a note as to what changed and when

[gl:/configure router "Base" interface "system"]
A:admin@R1# ipv4 primary
[gl:/configure router "Base" interface "system" ipv4 primary]
A:admin@R1# annotate "Changed on 18-Aug-23 (Was 192.168.1.1)" address
*[gl:/configure router "Base" interface "system" ipv4 primary]
A:admin@R1# address 1.1.1.1
*[gl:/configure router "Base" interface "system" ipv4 primary]

Let’s Look at the resulting configuration

A:admin@R1# /configure router interface "system"
*[gl:/configure router "Base" interface "system"]
A:admin@R1# info
    ipv4 {
        primary {
            # comment: Changed on 18-Aug-23 (Was 192.168.1.1)
            address 1.1.1.1
            prefix-length 32
        }
    }

Before we commit this change we can see that this documentation matches the changes, however it will start part of the configuration and can be reviewed by others in the future

*[gl:/configure router "Base" interface "system"]
A:admin@R1# compare
    ipv4 {
        primary {
-           address 192.168.1.1
+           # comment: Changed on 18-Aug-23 (Was 192.168.1.1)
+           address 1.1.1.1
        }
    }
*[gl:/configure router "Base" interface "system"]

Configuration Groups

Config Groups enable you to create resuable configurable blocks that are defined once and used multiple times. This should result in:

  • smaller and easier to read configurations
  • less chance of a “fat fingered” mistake when copying configuration blocks across sections
  • changes to the configuration group – get propogated to the other things consuming it

Below is an example of a configuration group infra-if-v4.

The regular expression “<.*>” will match any string (in a production environment this may be too aggressive and may need to be modified to something like “<to_pe-.*>” to only match interfaces that are similar to “to_pe-123”

[/]
A:admin@R1# configure global
INFO: CLI #2054: Entering global configuration mode
[gl:/configure]
A:admin@R1# groups
[gl:/configure groups]
A:admin@R1# info
    group "infra-if-v4" {
        router "<.*>" {
            interface "<.*>" {
                ipv4 {
                    icmp {
                        mask-reply false
                        redirects {
                            admin-state disable
                        }
                        unreachables {
                            number 10
                            seconds 10
                        }
                    }
                    urpf-check {
                        mode loose
                    }
                    bfd {
                        admin-state enable
                        transmit-interval 100
                        receive 100
                        multiplier 3
                        type auto
                    }
                    primary {
                        prefix-length 31
                    }
                    neighbor-discovery {
                        proactive-refresh true
                    }
                }
            }
        }
    }
[gl:/configure groups]

In this case, this takes a bunch of configuration boiler plate that we want all relevant interfaces to share. Interface “to-R2” will be created

[gl:/configure groups]
A:admin@R1# /configure router interface to-R2
*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# port 1/1/c1/1
*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# ipv4 primary address 172.16.0.1
*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# apply-groups "infra-if-v4"
*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# admin-state enable
*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# info
    apply-groups ["infra-if-v4"]
    admin-state enable
    port 1/1/c1/1
    ipv4 {
        primary {
            address 172.16.0.1
        }
    }

Through the info instruction we can see what was specifically configured in that interface, however we cannot review what the configuration pulled in from the configuration group unless we use the inheritance keyword

*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# info inheritance
    apply-groups ["infra-if-v4"]
    admin-state enable
    port 1/1/c1/1
    ipv4 {
        icmp {
            ## inherited: from group "infra-if-v4"
            mask-reply false
            redirects {
                ## inherited: from group "infra-if-v4"
                admin-state disable
            }
            unreachables {
                ## inherited: from group "infra-if-v4"
                number 10
                ## inherited: from group "infra-if-v4"
                seconds 10
            }
        }
        ## inherited: from group "infra-if-v4"
        urpf-check {
            ## inherited: from group "infra-if-v4"
            mode loose
        }
        bfd {
            ## inherited: from group "infra-if-v4"
            admin-state enable
            ## inherited: from group "infra-if-v4"
            transmit-interval 100
            ## inherited: from group "infra-if-v4"
            receive 100
            ## inherited: from group "infra-if-v4"
            multiplier 3
            ## inherited: from group "infra-if-v4"
            type auto
        }
        primary {
            address 172.16.0.1
            ## inherited: from group "infra-if-v4"
            prefix-length 31
        }
        neighbor-discovery {
            ## inherited: from group "infra-if-v4"
            proactive-refresh true
        }
    }

The messaging ## inherited: from group “infra-if-v4” is particularly helpful if there are multiple groups applied and you wish to see which part contributed to that part of the configuration, however if it clutters the output too much it is possible to filter it out

*[gl:/configure router "Base" interface "to-R2"]
A:admin@R1# info inheritance | match "##" invert-match
    apply-groups ["infra-if-v4"]
    admin-state enable
    port 1/1/c1/1
    ipv4 {
        icmp {
            mask-reply false
            redirects {
                admin-state disable
            }
            unreachables {
                number 10
                seconds 10
            }
        }
        urpf-check {
            mode loose
        }
        bfd {
            admin-state enable
            transmit-interval 100
            receive 100
            multiplier 3
            type auto
        }
        primary {
            address 172.16.0.1
            prefix-length 31
        }
        neighbor-discovery {
            proactive-refresh true
        }
    }

This is just a taste of what configuration groups can bring. If you would like to see more I have developed a containerlab demonstration to show the power of configuration groups to support Ethernet Port, IP Interface and OSPF Interface related configuration. The containerlab topology and walkthrough is is available on my github