Network Automation using YANG Models across XE, XR, & NX

YANG Model Downloads with ncclient

Now that we have ydk-gen installed and ready to generate Python class bindings for interacting with devices via YANG models, we need to decide which models we create Python classes for. As part of this portion of the lab, we'd like to generate Python bindings based on models we download directly from the lab devices. This helps to demonstrate how the devices themselves authoritatively declare what YANG models they support, and moreover, can be used to craft the Python bindings based on their capability advertisements.

Script Name: ydk_models_get.py

For this exercise, create a new Python file in your /workspace directory called ydk_models_get.py

Imports Required

For this exercise we need re (Regular Expressions) and manager from ncclient.

    
        import re
        from ncclient import manager
    

We will use Python's re module for extracting model names from the capability list returned, and ncclient as the transport mechanism for mediating our YANG-based model queries to the device.

Create the ncclient manager

In order to determine what YANG models the device supports, we must first instantiate an ncclient manager instance. We will use this manager to request the capabilities listing from the devices. For this exercise we choose to use the IOS-XRv device (xr), however the same technique could be applied to any device with YANG get_schema() capabilities.

    
        # Context manager keeping ncclient connection alive for the duration of
        # the context.
        with manager.connect(
            host='10.2.100.12',     # IP address of the XR device in your pod
            port=830,              # Port to connect to
            username='admin',      # SSH Username
            password='cisco.123',  # SSH Password
            hostkey_verify=False   # Allow unknown hostkeys not in local store
        ) as m:  # Context manager reference, i.e. instance of connected manager
            # The rest of your script code should go here.
            pass
    

With the manager created, we will now proceed to discover YANG models that the device supports.

Collect Model Capabilities

We will now ask ncclient to poll the devices capabilities, storing these in a list for later use.

    
    capabilities = []
    # Write each capability to console
    for capability in m.server_capabilities:
        # Print the capability
        print("Capability: %s" % capability)
        # Store the capability list for later.
        capabilities.append(capability)

    # Sort the list alphabetically.
    capabilities = sorted(capabilities)
    

Extract Supported Named YANG Models

Now that we have the capabilities list, we need to post-process them to extract the specific set of YANG model names that the device supports. Pay mind to the Python comment explaining the use of the regular expression.

    
    # Sort the list alphabetically.
    capabilities = sorted(capabilities)
    # List of modules that we store for use later
    modules = []
    # Iterate server capabilities and extract supported modules.
    for capability in capabilities:
        # Scan the capabilities and extract modules via this regex.
        # i.e., if this was the capability string:
        #   http://www.cisco.com/calvados/show_diag?module=show_diag&revision=2012-03-27
        # then:
        #   show_diag
        # .. would be the module printed.
        # Scan capability string for module
        supported_model = re.search('module=(.*)&', capability)
        # If module found in string, store it.
        if supported_model is not None:
            # Module string was found, store it.
            print("Supported Model: %s" % supported_model.group(1))
            # Store the module for later use.
            modules.append(supported_model.groups(0)[0])
    

Download the Models

For this module, we want to download the openconfig-interfaces native model schema files directly from the device. This is done as follows:

    
    # List of models that we want to download.
    # We will get the schema for each and write it to disk.
    models_desired = ['openconfig-extensions', 'openconfig-interfaces']
    # Iterate each desired model and write it to ./lab/models/
    for model in models_desired:
        # Get the model schema.
        schema = m.get_schema(model)
        # Open new file handle.
        with open("./{}.yang".format(model), 'w') as f:
            # Write schema
            f.write(schema.data)
    

The Full Script

The full script should look as follows:

    
        import re
        from ncclient import manager

        # Context manager keeping ncclient connection alive for the duration of
        # the context.
        with manager.connect(
            host='10.2.100.12',     # IP address of the XR device in your pod
            port=830,              # Port to connect to
            username='admin',      # SSH Username
            password='cisco.123',  # SSH Password
            hostkey_verify=False   # Allow unknown hostkeys not in local store
        ) as m:  # Context manager reference, i.e. instance of connected manager
            # The rest of your script code should go here.

            capabilities = []
            # Write each capability to console
            for capability in m.server_capabilities:
                # Print the capability
                print("Capability: %s" % capability)
                # Store the capability list for later.
                capabilities.append(capability)

            # Sort the list alphabetically.
            capabilities = sorted(capabilities)
            # List of modules that we store for use later
            modules = []
            # Iterate server capabilities and extract supported modules.
            for capability in capabilities:
                # Scan the capabilities and extract modules via this regex.
                # i.e., if this was the capability string:
                #   http://www.cisco.com/calvados/show_diag?module=show_diag&revision=2012-03-27
                # then:
                #   show_diag
                # .. would be the module printed.
                # Scan capability string for module
                supported_model = re.search('module=(.*)&', capability)
                # If module found in string, store it.
                if supported_model is not None:
                    # Module string was found, store it.
                    print("Supported Model: %s" % supported_model.group(1))
                    # Store the module for later use.
                    modules.append(supported_model.groups(0)[0])

            # List of models that we want to download.
            # We will get the schema for each and write it to disk.
            models_desired = ['openconfig-extensions', 'openconfig-interfaces']
            # Iterate each desired model and write it to ./lab/models/
            for model in models_desired:
                # Get the model schema.
                schema = m.get_schema(model)
                # Open new file handle.
                with open("./{}.yang".format(model), 'w') as f:
                    # Write schema
                    f.write(schema.data)
    

This full script will connect to the YANG-enabled IOS-XRv device, download it's NETCONF capability listing, extract a list of supported YANG modules using regular expressions, and then download from the device the openconfig-extensions and openconfig-interfaces .yang model definitions that we will use in a subsequent module to create Python classes via YDK.

Execute Python Script

    
        cd /workspace
        python3.6 ydk_models_get.py
    

After putting it all together and executing the script, you should have the openconfig-interfaces.yang and openconfig-extension.yang files in your container's workspace folder. Let's now explore one of the downloaded models.

Using the pyang Utility

The Python tool pyang is used to explore, validate, and translate yang models either user-created, cloned from public repositories, or downloaded from devices as we have done in this module.

Let us explore our downloaded openconfig-interfaces model a little with pyang. The syntax for viewing the YANG model tree using pyang is as follows:
    
         [root@234c5dde8ad6 workspace]# pyang -f tree openconfig-interfaces.yang
    

So what does openconfig-interfaces actually contain?
    
[root@234c5dde8ad6 workspace]# pyang -f tree openconfig-interfaces.yang
module: openconfig-interfaces
  +--rw interfaces
     +--rw interface* [name]
        +--rw name             -> ../config/name
        +--rw config
        |  +--rw type           identityref
        |  +--rw mtu?           uint16
        |  +--rw name?          string
        |  +--rw description?   string
        |  +--rw enabled?       boolean
        +--ro state
        |  +--ro type            identityref
        |  +--ro mtu?            uint16
        |  +--ro name?           string
        |  +--ro description?    string
        |  +--ro enabled?        boolean
        |  +--ro ifindex?        uint32
        |  +--ro admin-status    enumeration
        |  +--ro oper-status     enumeration
        |  +--ro last-change?    yang:timeticks
        |  +--ro counters
        |     +--ro in-octets?            yang:counter64
        |     +--ro in-unicast-pkts?      yang:counter64
        |     +--ro in-broadcast-pkts?    yang:counter64
        |     +--ro in-multicast-pkts?    yang:counter64
        |     +--ro in-discards?          yang:counter64
        |     +--ro in-errors?            yang:counter64
        |     +--ro in-unknown-protos?    yang:counter32
        |     +--ro out-octets?           yang:counter64
        |     +--ro out-unicast-pkts?     yang:counter64
        |     +--ro out-broadcast-pkts?   yang:counter64
        |     +--ro out-multicast-pkts?   yang:counter64
        |     +--ro out-discards?         yang:counter64
        |     +--ro out-errors?           yang:counter64
        |     +--ro last-clear?           yang:date-and-time
        +--rw hold-time
        |  +--rw config
        |  |  +--rw up?     uint32
        |  |  +--rw down?   uint32
        |  +--ro state
        |     +--ro up?     uint32
        |     +--ro down?   uint32
        +--rw subinterfaces
           +--rw subinterface* [index]
              +--rw index     -> ../config/index
              +--rw config
              |  +--rw index?         uint32
              |  +--rw name?          string
              |  +--rw description?   string
              |  +--rw enabled?       boolean
              +--ro state
                 +--ro index?          uint32
                 +--ro name?           string
                 +--ro description?    string
                 +--ro enabled?        boolean
                 +--ro ifindex?        uint32
                 +--ro admin-status    enumeration
                 +--ro oper-status     enumeration
                 +--ro last-change?    yang:timeticks
                 +--ro counters
                    +--ro in-octets?            yang:counter64
                    +--ro in-unicast-pkts?      yang:counter64
                    +--ro in-broadcast-pkts?    yang:counter64
                    +--ro in-multicast-pkts?    yang:counter64
                    +--ro in-discards?          yang:counter64
                    +--ro in-errors?            yang:counter64
                    +--ro in-unknown-protos?    yang:counter32
                    +--ro out-octets?           yang:counter64
                    +--ro out-unicast-pkts?     yang:counter64
                    +--ro out-broadcast-pkts?   yang:counter64
                    +--ro out-multicast-pkts?   yang:counter64
                    +--ro out-discards?         yang:counter64
                    +--ro out-errors?           yang:counter64
                    +--ro last-clear?           yang:date-and-time
[root@234c5dde8ad6 models]#
    
The pyang utility is incredibly powerful with its robust feature-set for exploring and manipulating with YANG models. We highly encourage you to explore its documentation and capabilities here.

We will now use these downloaded schema files as the basis for the creation of the Python bindings using ydk-gen.