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

Elementary BGP with YDK + OpenConfig


Now that you have a basic understanding of YANG models and YDK, let's start to put this together such that we use OpenConfig models to retrieve operational data from your devices. In this module you will connect to a device and attempt to read in BGP neighbor state data using the pre-built OpenConfig models that are included as part of YDK.

Device Configuration

We have to learn to walk before we can run, so in this example we will start with the simplest of the simplest in terms of configuration to see how the model ties back to the device configuration.

Type xr to enter your XRv device and configure BGP on your XRv device as follows:

        
        conf
        router bgp 65002
        neighbor 10.1.1.1
        commit
        end
        
    

Your XR config is about as simple as it could possibly be. We set the AS and defined one neighbor, and added nothing else. Let's see if OpenConfig's YANG models from YDK yield the same operational data when reading it using NETCONF and the Python bindings.

Create New Python File

Return to Visual Studio Code and create a new Python file called ydk_get_bgp.py. You can copy this file name below for ease.

ydk_get_bgp.py

YDK Imports

For this exercise, you need to import the openconfig_bgp model, the CRUDService for performing the model reads, and also the NetconfServiceProvider for the connector of the model data between the Python client and the device.

        
            from ydk.models.openconfig import openconfig_bgp
            from ydk.services import CRUDService
            from ydk.providers import NetconfServiceProvider
            

Instantiate the NetconfServiceProvider

To interact with the device, we will use the NetconfServiceProvider that is packaged within ydk.providers. The service provider acts as the mediator of the model data between the device and our client using NETCONF for transport operations.

        
            # Mapping of device names to IPs
            duts = {
                "xrv": '10.2.100.12',
            }

            # User the XRV for this simple exercise.
            ip = duts['xrv']

            # Get the provider and CRUD for the device.
            # create NETCONF session
            provider = NetconfServiceProvider(address=ip, port=830, username='admin', password='cisco.123', protocol='ssh')
            

Enable Full Debug Logging

YDK has the a built-in ability to enable debug logging. Enable and attach to debug logging to see what's going on under the hood inside YDK.

        
            # Attach to the YDK Logger
            import logging
            logger = logging.getLogger("ydk")
            logger.setLevel(logging.DEBUG)
            handler = logging.StreamHandler()
            formatter = logging.Formatter(("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
            handler.setFormatter(formatter)
            logger.addHandler(handler)
            

Instantiate the CRUD Service

The CRUD service (Create, Read, Update, Delete) is the object that takes the underlying provider and YANG model desired and interacts with the device.

        
            # Instantiate the CRUD service to be used for interacting with devvice.
            crud = CRUDService()
            

Instantiate the BGP Model

In order to use the OpenConfig BGP models that come with YDK, we must first instantiate a new instance of the top-level model.

        
            # Create BGP model from OpenConfig
            bgp_model = openconfig_bgp.Bgp()
            

Populate the BGP Model Instance

With the model instantiated, we now use all three components together to read the operational data from the device. The CRUD service takes a provider, and a model such that the service reads the requested model data (BGP) over the specified transport mechanism (NETCONF).

        
            # Get the BGP data from the device
            bgp = crud.read(provider, bgp_model)
            

At this point in your code during runtime, the "bgp" instance that we instantiated earlier will have been popualted with the operational data from the device.

Check Global AS

Recall that we configured the global AS to the value of 12345. Let's see if we can observe this from our populated model. Place this print statement after the crud.read() operation so that you can verify this operational data:

        
            # Check the global AS number as reported by YDK
            print("Global AS:" + bgp.global_.state.as_)
            

Upon execution of your Python code, the model should report the same AS number that you configured over CLI.

Iterate Configured Neighbors

To confirm that we were able to read BGP operational data using OpenConfig and YANG models from YDK, we can iterate the neighbors attribute of BGP using a for loop.

        
            # Print each neighbor and their addresses.
            for neighbor in bgp.neighbors.neighbor:
                print(f"Neighbor: {neighbor.state.neighbor_address}, State: {neighbor.state.session_state}")
            

The Full Script

Your script should look like this:

        
        from ydk.models.openconfig import openconfig_bgp
        from ydk.services import CRUDService
        from ydk.providers import NetconfServiceProvider

        # Mapping of device names to IPs
        duts = {
            "xrv": '10.2.100.12',
        }

        # User the XRV for this simple exercise.
        ip = duts['xrv']

        # Get the provider and CRUD for the device.
        # create NETCONF session
        provider = NetconfServiceProvider(address=ip, port=830, username='admin', password='cisco.123', protocol='ssh')

        # Attach to the YDK Logger
        import logging
        logger = logging.getLogger("ydk")
        logger.setLevel(logging.DEBUG)
        handler = logging.StreamHandler()
        formatter = logging.Formatter(("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
        handler.setFormatter(formatter)
        logger.addHandler(handler)

        # Instantiate the CRUD service to be used for interacting with devvice.
        crud = CRUDService()

        # Create BGP model from OpenConfig
        bgp_model = openconfig_bgp.Bgp()

        # Get the BGP data from the device
        bgp = crud.read(provider, bgp_model)

        # Check the global AS number as reported by YDK
        print("Global AS:" + bgp.global_.state.as_)

        # Print each neighbor and their addresses.
        for neighbor in bgp.neighbors.neighbor:
            print(f"Neighbor: {neighbor.state.neighbor_address}, State: {neighbor.state.session_state}")
        
    

Execute Python Script

Remember to save your new Python script with Ctrl+s, then return to your container environment in Terminal to execute you new script.

        
            cd /workspace/
            python3.6 ydk_get_bgp.py
        
    

Observe YANG Data Read via YDK

If all steps were followed, you should see that the BGP model reports the BGP ASN and 1 neighbor as being configured, with a state of None (because this is an example with no peer on the other end).

There will be a ton of debug output from YDK corresponding to each of the internal RPCs performed by YDK as it interacts with the device to carry out your request using it's CRUD engine. Near the bottom of the output you should see text similar to the following where you'll see the Global AS and Neighbor:

        
            2018-06-10 03:58:49,003 - ydk - DEBUG - Creating entity leaf neighbor-address of value '10.1.1.1' in parent /openconfig-bgp:bgp/neighbors/neighbor[neighbor-address='10.1.1.1']/state
            2018-06-10 03:58:49,003 - ydk - DEBUG - Created leaf
            Global AS:65002
            Neighbor: 10.1.1.1, State: None
            2018-06-10 03:58:49,003 - ydk - INFO - Disconnected from device
            2018-06-10 03:58:49,003 - ydk - DEBUG - Trace: Writing message (session 655893890): <?xml version="1.0" encoding="UTF-8"?>
            <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="2">
              <close-session/>
            </rpc>

            2018-06-10 03:58:49,003 - ydk - DEBUG - Trace: Received message (session 655893890): <?xml version="1.0"?>
            <rpc-reply message-id="2" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
             <ok/>
            </rpc-reply>

            [root@eb7f7fa515f6 workspace]#
        
    

To summarize, in this module we imported the Openconfig BGP model that is included in YDK, instantiated a NetconfServiceProvider pointing at a device, instantiated a CRUD service for mediating model operations, and instantiated the model that we wanted to interact with. Once those components were all instantiated, we performed a READ operation, supplying the CRUD service the provider and model we wanted to read data from, and printed basic data as reported by the populated data model.

Cleanup

Log back into the XRv instance using xe and remove the BGP configuration that was applied at the beginning of the lab.

        
            conf
            no router bgp 65002
            commit
            end
        
    

With fundamentals out of the way, let's move on to more advanced and useful examples where you'll now configure BGP on using YDK-Python.