Step 3: Creating YANG-XML from config snippets for ZTP

8 minutes read

Final intended configuration for the routers

The final configuration required for each of the routers post ZTP is already present on the ZTP node.

SSH to the ZTP node and drop into the /var/www/html/configs/ directory:

tesuto@ztp:~$ cd /var/www/html/configs/
tesuto@ztp:/var/www/html/configs$ tree .
.
├── rtr1.config
├── rtr2.config
├── rtr3.config
└── rtr4.config

0 directories, 4 files
tesuto@ztp:/var/www/html/configs$ 

Since Day0 group is only dealing with rtr2 initially, let’s look at the rtr2 configuration:

tesuto@ztp:/var/www/html/configs$ cat rtr2.config 
!! IOS XR Configuration version = 6.5.2.28I
!! Last configuration change at Mon Feb 11 04:15:57 2019 by ZTP
!
hostname rtr2
domain name cisco.local
domain name-server 8.8.8.8
username rtrdev
 group root-lr
 group cisco-support
 secret 5 $1$mtK/$tVi/gbwfgZu6imOoriwxV.
!
tpa
 vrf default
  address-family ipv4
   default-route mgmt
   update-source dataports MgmtEth0/RP0/CPU0/0
  !
  address-family ipv6
   default-route mgmt
   update-source dataports MgmtEth0/RP0/CPU0/0
  !
 !
!
call-home
 service active
 contact smart-licensing
 profile CiscoTAC-1
  active
  destination transport-method http
 !
!
interface Loopback0
 ipv4 address 172.16.2.1 255.255.255.255
!
interface MgmtEth0/RP0/CPU0/0
 ipv4 address 100.96.0.16 255.240.0.0
 no shutdown
!
interface GigabitEthernet0/0/0/0
 ipv4 address 10.2.1.20 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/1
 ipv4 address 10.4.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/2
 ipv4 address 10.5.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/3
 shutdown
!
interface GigabitEthernet0/0/0/4
 shutdown
!
interface GigabitEthernet0/0/0/5
 shutdown
!
interface GigabitEthernet0/0/0/6
 shutdown
!
interface GigabitEthernet0/0/0/7
 shutdown
!
interface GigabitEthernet0/0/0/8
 shutdown
!
interface GigabitEthernet0/0/0/9
 shutdown
!
mpls static
 interface GigabitEthernet0/0/0/0
 interface GigabitEthernet0/0/0/1
 interface GigabitEthernet0/0/0/2
!
router static
 address-family ipv4 unicast
  0.0.0.0/0 100.96.0.1
 !
!
grpc
 port 57777
 no-tls
 service-layer
 !
!
netconf-yang agent
 ssh
!
lldp
!
ssh server v2
ssh server vrf default
ssh server netconf vrf default
end
tesuto@ztp:/var/www/html/configs$ 


This is the final configuration that must be achieved on rtr2 as a result of ZTP.

Base Configuration on each router

We put a base configuration (username, tpa config) on each router and certain parts of the configuration (like ssh and netconf) are enabled during the init process for ncclient. Thus the common base configuration for all the routers is:


!! IOS XR Configuration version = 6.5.2.28I
!! Last configuration change at Mon Feb 11 04:15:57 2019 by ZTP
!
domain name cisco.local
domain name-server 8.8.8.8
username rtrdev
 group root-lr
 group cisco-support
 secret 5 $1$mtK/$tVi/gbwfgZu6imOoriwxV.
!
tpa
 vrf default
  address-family ipv4
   default-route mgmt
   update-source dataports MgmtEth0/RP0/CPU0/0
  !
  address-family ipv6
   default-route mgmt
   update-source dataports MgmtEth0/RP0/CPU0/0
  !
 !
!
call-home
 service active
 contact smart-licensing
 profile CiscoTAC-1
  active
  destination transport-method http
 !
 !
interface MgmtEth0/RP0/CPU0/0
 shutdown
!
interface GigabitEthernet0/0/0/3
 shutdown
!
interface GigabitEthernet0/0/0/4
 shutdown
!
interface GigabitEthernet0/0/0/5
 shutdown
!
interface GigabitEthernet0/0/0/6
 shutdown
!
interface GigabitEthernet0/0/0/7
 shutdown
!
interface GigabitEthernet0/0/0/8
 shutdown
!
interface GigabitEthernet0/0/0/9
 shutdown
!
!
!
netconf-yang agent
 ssh
!
!
ssh server v2
ssh server vrf default
ssh server netconf vrf default
end



Config Snippet to be applied

The Management port configuration, default route and domain-name settings are applied by the DHCP server’s response, so we ignore these as well.

Taking a diff between expected CLI and the base CLI, we get the following:

rtr2.config.diff

hostname rtr2
!
interface Loopback0
 ipv4 address 172.16.2.1 255.255.255.255
!
interface GigabitEthernet0/0/0/0
 ipv4 address 10.2.1.20 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/1
 ipv4 address 10.4.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/2
 ipv4 address 10.5.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!
mpls static
 interface GigabitEthernet0/0/0/0
 interface GigabitEthernet0/0/0/1
 interface GigabitEthernet0/0/0/2
!
grpc
 port 57777
 no-tls
 service-layer
 !
!
lldp
!
end

This is the configuration that must be applied to rtr2 by the ZTP script.

Converting the required CLI into XML

Let’s break down the required CLI configuration into individual Yang models that we need to use:

  1. For the hostname CLI:

    !
    hostname rtr2
    

    The yang model to be used is: Cisco-IOS-XR-shellutil-cfg.yang

    Since this is an XR specific model, we’ve already converted the CLI snippet into XML:

    <config>
     <host-names xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg">
         <host-name>rtr2</host-name>
     </host-names>
    </config>
       
    

    This XML snippet is stored as hostname.xml on the webserver at /var/www/html/xml/rtr2/

  2. For the grpc CLI:

    !
    grpc
     port 57777
     no-tls
     service-layer
     !
    !
    

    The Yang model to be used is : Cisco-IOS-XR-man-ems-cfg.yang

    Again, being an XR specific model, we’ve generated the XML RPC for you:

    <config>
     <grpc xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-man-ems-cfg">
         <port>57777</port>
         <no-tls></no-tls>
         <enable></enable>
         <service-layer>
             <enable></enable>
         </service-layer>
     </grpc>
    </config>
    

    This XML snippet is stored as grpc_config.xml on the webserver at /var/www/html/xml/rtr2/

  3. For the mpls static CLI:

    !
    mpls static
    interface GigabitEthernet0/0/0/0
    interface GigabitEthernet0/0/0/1
    interface GigabitEthernet0/0/0/2
    !
       
    

    The Yang model to be used is : Cisco-IOS-XR-mpls-static-cfg.yang

    Being an XR specific model, we’ve generated the XML for you:

    <config>
     <mpls-static xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-mpls-static-cfg">
         <enable></enable>
         <interfaces>
             <interface>
                 <interface-name>GigabitEthernet0/0/0/0</interface-name>
             </interface>
             <interface>
                 <interface-name>GigabitEthernet0/0/0/1</interface-name>
             </interface>
             <interface>
                  <interface-name>GigabitEthernet0/0/0/2</interface-name>
                </interface>
         </interfaces>
     </mpls-static>
    </config> 
    

    This XML snippet is stored as mpls_static.xml on the webserver at /var/www/html/xml/rtr2/

This leaves us with two CLI snippets:

lldp configuration:

!
lldp
!

and data port configurations:

!
interface GigabitEthernet0/0/0/0
 ipv4 address 10.2.1.20 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/1
 ipv4 address 10.4.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!
interface GigabitEthernet0/0/0/2
 ipv4 address 10.5.1.10 255.255.255.0
 ipv6 enable
 no shutdown
!


Both of these CLI snippets have Openconfig Models available to generate the XML for them. These are:

  • LLDP Openconfig model: openconfig-lldp.yang
  • Interface Openconfig Model: openconfig-interfaces.yang

Getting Access to Yang models

Clone the code-samples repository to get access to the Yang models compatible with the release of the vendor with appropriate deviations implemented:

tesuto@ztp:~$ 
tesuto@ztp:~$ git clone https://github.com/nanog75/code-samples
Cloning into 'code-samples'...
remote: Enumerating objects: 1603, done.
remote: Counting objects: 100% (1603/1603), done.
remote: Compressing objects: 100% (625/625), done.
remote: Total 1603 (delta 1001), reused 1551 (delta 964), pack-reused 0
Receiving objects: 100% (1603/1603), 12.74 MiB | 7.52 MiB/s, done.
Resolving deltas: 100% (1001/1001), done.
Checking out files: 100% (2650/2650), done.
tesuto@ztp:~$ 
tesuto@ztp:~$ 
tesuto@ztp:~$ cd code-samples/ztp/yang/
tesuto@ztp:~/code-samples/ztp/yang$ 
tesuto@ztp:~/code-samples/ztp/yang$ 
tesuto@ztp:~/code-samples/ztp/yang$ 
tesuto@ztp:~/code-samples/ztp/yang$ ls
CISCO-ENTITY-FRU-CONTROL-MIB.yang                     Cisco-IOS-XR-manageability-object-tracking-cfg.yang
Cisco-IOS-XR-PRIVATE-ocacl-ipv4-oper-sub1.yang        Cisco-IOS-XR-manageability-object-tracking-datatypes.yang
Cisco-IOS-XR-PRIVATE-ocacl-ipv4-oper.yang             Cisco-IOS-XR-manageability-object-tracking-oper-sub1.yang
Cisco-IOS-XR-PRIVATE-ocacl-ipv6-oper-sub1.yang        Cisco-IOS-XR-manageability-object-tracking-oper.yang
Cisco-IOS-XR-PRIVATE-ocacl-ipv6-oper.yang             Cisco-IOS-XR-manageability-perfmgmt-cfg.yang
Cisco-IOS-XR-PRIVATE-ocacl-l2-oper-sub1.yang          Cisco-IOS-XR-manageability-perfmgmt-datatypes.yang
Cisco-IOS-XR-PRIVATE-ocacl-l2-oper.yang               Cisco-IOS-XR-manageability-perfmgmt-oper-sub1.yang
Cisco-IOS-XR-PRIVATE-ocni-bgp-oper-sub1.yang          Cisco-IOS-XR-manageability-perfmgmt-oper.yang
Cisco-IOS-XR-PRIVATE-ocni-bgp-oper.yang               Cisco-IOS-XR-mdrv-lib-cfg.yang
Cisco-IOS-XR-PRIVATE-ocni-bpm-oper-sub1.yang          Cisco-IOS-XR-mediasvr-linux-oper-sub1.yang
Cisco-IOS-XR-PRIVATE-ocni-bpm-oper.yang               Cisco-IOS-XR-mediasvr-linux-oper.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-rsvp-oper-sub1.yang    Cisco-IOS-XR-mpls-io-cfg.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-rsvp-oper.yang         Cisco-IOS-XR-mpls-io-oper-sub1.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-static-oper-sub1.yang  Cisco-IOS-XR-mpls-io-oper.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-static-oper.yang       Cisco-IOS-XR-mpls-ldp-cfg-datatypes.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-te-oper-sub1.yang      Cisco-IOS-XR-mpls-ldp-cfg.yang
Cisco-IOS-XR-PRIVATE-ocni-mpls-te-oper.yang           Cisco-IOS-XR-mpls-ldp-oper-datatypes.yang
Cisco-IOS-XR-Subscriber-infra-subdb-oper-sub1.yang    Cisco-IOS-XR-mpls-ldp-oper-sub1.yang
Cisco-IOS-XR-Subscriber-infra-subdb-oper-sub2.yang    Cisco-IOS-XR-mpls-ldp-oper-sub2.yang
Cisco-IOS-XR-Subscriber-infra-subdb-oper.yang         Cisco-IOS-XR-mpls-ldp-oper-sub3.yang
Cisco-IOS-XR-aaa-aaacore-cfg.yang                     Cisco-IOS-XR-mpls-ldp-oper.yang
Cisco-IOS-XR-aaa-diameter-base-mib-cfg.yang           Cisco-IOS-XR-mpls-lsd-cfg.yang
Cisco-IOS-XR-aaa-diameter-cfg.yang                    Cisco-IOS-XR-mpls-lsd-oper-sub1.yang
Cisco-IOS-XR-aaa-diameter-oper-sub1.yang              Cisco-IOS-XR-mpls-lsd-oper.yang
Cisco-IOS-XR-aaa-diameter-oper.yang                   Cisco-IOS-XR-mpls-oam-cfg.yang
Cisco-IOS-XR-aaa-lib-cfg.yang                         Cisco-IOS-XR-mpls-oam-oper-sub1.yang
Cisco-IOS-XR-aaa-lib-datatypes.yang                   Cisco-IOS-XR-mpls-oam-oper.yang
Cisco-IOS-XR-aaa-locald-admin-cfg.yang                Cisco-IOS-XR-mpls-static-cfg.yang
Cisco-IOS-XR-aaa-locald-cfg.yang                      Cisco-IOS-XR-mpls-static-oper-sub1.yang
Cisco-IOS-XR-aaa-locald-oper-sub1.yang                Cisco-IOS-XR-mpls-static-oper.yang
Cisco-IOS-XR-aaa-locald-oper.yang                     Cisco-IOS-XR-mpls-te-cfg.yang
Cisco-IOS-XR-aaa-nacm-cfg.yang                        Cisco-IOS-XR-mpls-te-datatypes.yang
Cisco-IOS-XR-aaa-nacm-oper-sub1.yang                  Cisco-IOS-XR-mpls-te-oper-sub1.yang
Cisco-IOS-XR-aaa-nacm-oper.yang                       Cisco-IOS-XR-mpls-te-oper-sub2.yang
Cisco-IOS-XR-aaa-protocol-radius-cfg.yang             Cisco-IOS-XR-mpls-te-oper-sub3.yang
Cisco-IOS-XR-aaa-protocol-radius-oper-sub1.yang       Cisco-IOS-XR-mpls-te-oper-sub4.yang
Cisco-IOS-XR-aaa-protocol-radius-oper-sub2.yang       Cisco-IOS-XR-mpls-te-oper-sub5.yang
Cisco-IOS-XR-aaa-protocol-radius-oper.yang            Cisco-IOS-XR-mpls-te-oper-sub6.yang
Cisco-IOS-XR-aaa-tacacs-cfg.yang                      Cisco-IOS-XR-mpls-te-oper


..........


Install pyang:

tesuto@ztp:~$ 
tesuto@ztp:~$ sudo pip3 install pyang
The directory '/home/tesuto/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/tesuto/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting pyang
  Downloading https://files.pythonhosted.org/packages/43/d3/0cc5538d83db3216f4c5acbfa5849a601dfe2e80e5fade872d5e6d5ee7d7/pyang-1.7.8-py2.py3-none-any.whl (447kB)
    100% |████████████████████████████████| 450kB 2.6MB/s 
Requirement already satisfied: lxml in ./.local/lib/python3.6/site-packages (from pyang)
Installing collected packages: pyang
Successfully installed pyang-1.7.8
tesuto@ztp:~$ 

Parse the yang model using pyang

Let’s look at the lldp yang model for example:


tesuto@ztp:~/code-samples/ztp/yang$ 
tesuto@ztp:~/code-samples/ztp/yang$ pyang -f tree openconfig-lldp.yang 
module: openconfig-lldp
  +--rw lldp
     +--rw config
     |  +--rw enabled?                      boolean
     |  +--rw hello-timer?                  uint64
     |  +--rw suppress-tlv-advertisement*   identityref
     |  +--rw system-name?                  string
     |  +--rw system-description?           string
     |  +--rw chassis-id?                   string
     |  +--rw chassis-id-type?              oc-lldp-types:chassis-id-type
     +--ro state
     |  +--ro enabled?                      boolean
     |  +--ro hello-timer?                  uint64
     |  +--ro suppress-tlv-advertisement*   identityref
     |  +--ro system-name?                  string
     |  +--ro system-description?           string
     |  +--ro chassis-id?                   string
     |  +--ro chassis-id-type?              oc-lldp-types:chassis-id-type
     |  +--ro counters
     |     +--ro frame-in?           yang:counter64
     |     +--ro frame-out?          yang:counter64
     |     +--ro frame-error-in?     yang:counter64
     |     +--ro frame-discard?      yang:counter64
     |     +--ro tlv-discard?        yang:counter64
     |     +--ro tlv-unknown?        yang:counter64
     |     +--ro last-clear?         yang:date-and-time
     |     +--ro tlv-accepted?       yang:counter64
     |     +--ro entries-aged-out?   yang:counter64
     +--rw interfaces
        +--rw interface* [name]
           +--rw name         -> ../config/name
           +--rw config
           |  +--rw name?      oc-if:base-interface-ref
           |  +--rw enabled?   boolean
           +--ro state
           |  +--ro name?       oc-if:base-interface-ref
           |  +--ro enabled?    boolean
           |  +--ro counters
           |     +--ro frame-in?          yang:counter64
           |     +--ro frame-out?         yang:counter64
           |     +--ro frame-error-in?    yang:counter64
           |     +--ro frame-discard?     yang:counter64
           |     +--ro tlv-discard?       yang:counter64
           |     +--ro tlv-unknown?       yang:counter64
           |     +--ro last-clear?        yang:date-and-time
           |     +--ro frame-error-out?   yang:counter64
           +--ro neighbors
              +--ro neighbor* [id]
                 +--ro id              -> ../state/id
                 +--ro config
                 +--ro state
                 |  +--ro system-name?               string
                 |  +--ro system-description?        string
                 |  +--ro chassis-id?                string
                 |  +--ro chassis-id-type?           oc-lldp-types:chassis-id-type
                 |  +--ro id?                        string
                 |  +--ro age?                       uint64
                 |  +--ro last-update?               int64
                 |  +--ro port-id?                   string
                 |  +--ro port-id-type?              oc-lldp-types:port-id-type
                 |  +--ro port-description?          string
                 |  +--ro management-address?        string
                 |  +--ro management-address-type?   string
                 +--ro custom-tlvs
                 |  +--ro tlv* [type oui oui-subtype]
                 |     +--ro type           -> ../state/type
                 |     +--ro oui            -> ../state/oui
                 |     +--ro oui-subtype    -> ../state/oui-subtype
                 |     +--ro config
                 |     +--ro state
                 |        +--ro type?          int32
                 |        +--ro oui?           string
                 |        +--ro oui-subtype?   string
                 |        +--ro value?         binary
                 +--ro capabilities
                    +--ro capability* [name]
                       +--ro name      -> ../state/name
                       +--ro config
                       +--ro state
                          +--ro name?      identityref
                          +--ro enabled?   boolean
tesuto@ztp:~/code-samples/ztp/yang$ 

The highlighted entries above indicate the exact fields we need to capture in the final XML snippet. So this gets converted into:

<config>
        <lldp xmlns="http://openconfig.net/yang/lldp">
		<config>
			<enabled>true</enabled>
		</config>
	</lldp>
</config>

with xmlns="http://openconfig.net/yang/" being the generic openconfig namespace denotion.

Do the same on openconfig-interfaces.yang and create the last XML snippet required.

Setting up the infra

So we have the following XML files, potentially for rtr2:

lldp_oc.xml, 
interfaces_oc.xml, 
mpls_static.xml, 
hostname.xml, 
grpc_config.xml

Store these files on the web server at /var/www/html/xml/rtr2

If you take a look at the existing script at /var/www/html/scripts/ztp_ncclient.py, there is already a Serial-Number to router name Map in each script.

Use the map to determine unique urls per router. Download the files and use ncclient to apply them and check for error conditions.

ZTP automation requires some patience to set up even if the actual script is fairly simple.
Hang in there and it’ll be worth it!