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

Configure BGP with YDK-Python on NX


For Nexus, you are going to follow a similar approach but incorporate some additional YANG tooling and techniques to demonstrate the various ways you can configure and drive YDK using YANG. In this exercise, you will configure an eBGP peer on you NX device toward your XR device using YDK, YANG, and OpenConfig BGP models.

Enabling Feature BGP

On Nexus, features must first be enabled, this is true for the BGP feature. Currently, as done when you configured OSPF, the Nexus native models are the only YANG models that support enabling features, thus we must use those to enable the BGP feature here. Further, Nexus native YANG models are currently not supported in YDK, but are coming in the next few months. For now, let's enable the BGP feature using ncclient like you did for OSPF. It is important to note here, that given this is all Python, this script you are about to develop using ncclient and the sub-sequent script that you will develop below using YDK could be in the same Python file with import from both packages. We simply break the files up in this lab for feasibility.

Create New Script

Return to VS Code and create a new script called ncclient_nx_bgp_enable.py

ncclient_nx_bgp_enable.py

Enabling BGP on NX with ncclient

The ncclient script structure should be looking fairly familiar to you at this point. This will be a NETCONF edit-config.

        
        from ncclient import manager

        device = {"ip": "10.2.100.13", "port": "830", "platform": "nexus",}

        with manager.connect(host=device['ip'], port=device['port'], username='admin',
                             password='cisco.123', hostkey_verify=False,
                             device_params={'name': device['platform']},
                             look_for_keys=False, allow_agent=False) as m:

            # RPC call to enable feature bgp
            rpc_call = '''
                        <config>
                            <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                            <fm-items>
                                <bgp-items>
                                <adminSt>enabled</adminSt>
                                </bgp-items>
                            </fm-items>
                            </System>
                        </config>
                        '''

            # Apply the config change
            reply = m.edit_config(rpc_call, target="running")
            print(reply)
        
    

Execute Script

Remember to save your code with Ctrl+s, then return to your container dev environment. Execute your script to enable the BGP feature on NX.

        
            cd /workspace/
            python3.6 ncclient_nx_bgp_enable.py
        
    

You should see an XML RPC Reply with status ok. The BGP feature should now be enabled on your Nexus device.

        
    [root@73dfdfffbd8c workspace]# python3.6 ncclient_nx_bgp_enable.py
    <?xml version="1.0" encoding="UTF-8"?>
    <rpc-reply xmlns:if="http://www.cisco.com/nxos:1.0:if_manager" xmlns:nfcli="http://www.cisco.com/nxos:1.0:nfcli" xmlns:nxos="http://www.cisco.com/nxos:1.0" xmlns:vlan_mgr_cli="http://www.cisco.com/nxos:1.0:vlan_mgr_cli" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:178fb64b-7d8f-4ea3-befd-4ce8e10d1ffa">
        <ok/>
    </rpc-reply>

    [root@73dfdfffbd8c workspace]#

        
    

Clone the YANG Repository

Clone the public YANG repository into your container workspace. The lab will demonstrate how to instruct your code to use YANG models explicitly located on your local filesystem as opposed to models that YDK downloads on-the-fly. This is a useful technique to be aware of in the event that you want to test out specific models against specific software versions or provide your own models.

        
            cd /workspace/
            git clone https://github.com/YangModels/yang.git
        
    
        
        [root@ef80bf8e25fb workspace]# git clone https://github.com/YangModels/yang.git
        Cloning into 'yang'...
        remote: Counting objects: 13445, done.
        remote: Compressing objects: 100% (187/187), done.
        remote: Total 13445 (delta 353), reused 529 (delta 353), pack-reused 12905
        Receiving objects: 100% (13445/13445), 23.11 MiB | 4.85 MiB/s, done.
        Resolving deltas: 100% (9076/9076), done.
        Checking out files: 100% (12358/12358), done.
        
    

Note: You may observe the following when trying to attempt the clone step for the first time:

        
        [root@73dfdfffbd8c workspace]# git clone https://github.com/YangModels/yang.git
        Cloning into 'yang'...
        fatal: unable to access 'https://github.com/YangModels/yang.git/': Could not resolve host: github.com; Unknown error
        
    

If you see this message, run the command a 2nd time and the clone will proceed.

Adding Additional Model Patches from CiscoDevNet

For the NX device you are configuring, some additional models are required that aren't by default included in the /vendor/cisco/nx YANG path, but you can get them from DevNet.

            
                wget https://github.com/CiscoDevNet/ydk-gen/files/1817262/models.zip
                unzip models.zip
                cp ./models/* yang/vendor/cisco/nx/7.0-3-I7-3/
            
        

When prompted to overwrite the existing file, respond with Yes by pressing y and then enter as shown below:

            
            [root@73dfdfffbd8c workspace]# cp ./models/* yang/vendor/cisco/nx/7.0-3-I7-3/
            cp: overwrite 'yang/vendor/cisco/nx/7.0-3-I7-3/Cisco-NX-OS-device.yang'? y
            [root@73dfdfffbd8c workspace]#
            
        
This adds the ietf-netconf-acm, ietf-netconf, and YDK YANG models required for the full NETCONF interaction for NX BGP configuration. This is only required when configuring openconfig-bgp using the manual instantiation of the NetConfServiceProvider with a specific YANG model repository configuration. Typically get_schema() handles the dependency resolution step to aquire the correct YANG model dependencies. Note: This issue was only observed by us when using NX7-3v on YDK 0.7.1, later versions should have this resolved. We kept this step in because it helps demonstrate how one can override the models YDK downloads with instead models that a user specifies, using a combination of models cloned from official GitHub and patched models from CiscoDevNet.

Create New Python File

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

ydk_nx_bgp_conf.py

Imports Required

You will need to import the same CRUDservice, NetconfServiceProver, and openconfig_bgp model as before. In addition, for the full configuration sequence openconfig_bgp_types is also required. Note also that Repository is being imported from ydk.path. In this example we are going to explicitly specify which YANG models we supply for the underlying NETCONF Service provider to use, as opposed to relying on the models provided by the device via YDKs interaction with NETCONF get_schema() as was implicitly done for XRv. This is done to demonstrate how YDK can use models sourced directly from the device, but also models specified by the developer.

        
            # Imports required
            from ncclient import manager
            from ydk.services import NetconfService, Datastore, CRUDService
            from ydk.providers import NetconfServiceProvider
            from ydk.path import Repository
            from ydk.models.openconfig import openconfig_bgp
            from ydk.models.openconfig import openconfig_bgp_types as oc_bgp_types
            

Instantiate the NETCONF Provider

As we did in the previous module, you again need a NetConfServiceProvider, CRUD service, and instance of the BGP model instantiated. Note that in this script, we supply the repo string that indicates to the NetconfServiceProvider where to source the YANG models from when building the NETCONF RPCs that carry out the intentions of the developer relative to properties and attributes set on the openconfig_bgp.Bgp() model.

        
            # Define the path of the YANG models that we want to use.
            repo = Repository('/workspace/yang/vendor/cisco/nx/7.0-3-I7-3')

            # Mapping of device names to IPs
            duts = {
                "nx": '10.2.100.13',
            }

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

            # Instantiate the NetconfServiceProvider.
            provider = NetconfServiceProvider(
                repo=repo,
                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)

            # Create the netconf service.
            netconf = NetconfService()

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

Global BGP Configuration

Using the instantiated top-level BGP object from the OpenConfig model, configure the global BGP options as you did for XR that included configuring the AS and router-id leaf nodes as seen below:

        
            # Global configuration
            bgp.global_.config.as_ = 65003
            bgp.global_.config.router_id = '3.3.3.3'
            

Configure Peer Groups

We will now configure the peer groups just as was done for XR, except this time, only a single peer group given the NX device is only peering to the XR device:

        
            # Peer group configuration
            peer_group = bgp.peer_groups.PeerGroup()
            peer_group.peer_group_name = "eBGP-XR"
            peer_group.config.peer_group_name = "eBGP-XR"
            peer_group.config.peer_as = 65002
            bgp.peer_groups.peer_group.append(peer_group)
            

Add the BGP Peer

You still need to add the XR device as neighbor using the peer group. Lets continue to work with our BGP instance and add the desired neighbor objects, again this is setting leaf node within the neighbors list per the defined YANG model using object oriented dot-notation.

        
            # Add the XR neighbor
            neighbor = bgp.neighbors.Neighbor()
            neighbor.neighbor_address = "10.2.2.2"
            neighbor.config.neighbor_address = "10.2.2.2"
            neighbor.config.peer_group = "eBGP-XR"
            bgp.neighbors.neighbor.append(neighbor)
            

Commit the Changes

So far in your code, all you have done is populate your local model instance with the desired global properties and attributes that are desired to have configured on the device; you have not actually altered device state yet. To actually push the BGP configuration changes to your NX device, you will use the NetconfService instantiated earlier. In this module, you are using NetconfService as opposed to the CRUD service because it allows us to specify which datastore we are updating (running), whereas the CRUD service doesn't give us that flexibility and relies on the candidate datastore that the Nexus platform doesn't have at this time.

        
            # Commit the changes to the running data-store.
            netconf.edit_config(provider, Datastore.running, bgp)
            

Use NX Native YANG and ncclient to Add the Address Family

As mentioned previously, we have to fall use the NX native YANG models and ncclient to configure the address family. This is a footnote at the bottom regarding this. Add this final section to your script.

        
            with manager.connect(host=ip, port="830", username="admin",
                     password="cisco.123", hostkey_verify=False,
                     device_params={"name": "nexus"},
                     look_for_keys=False, allow_agent=False) as m:

                # RPC call to add BGP address-family to peer group
                rpc = '''
                        <config>
                            <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                            <bgp-items>
                                <inst-items>
                                    <adminSt>enabled</adminSt>
                                    <asn>65003</asn>
                                    <dom-items>
                                        <Dom-list>
                                            <name>default</name>
                                            <peercont-items>
                                                <PeerCont-list>
                                                    <name>eBGP-XR</name>
                                                    <adminSt>enabled</adminSt>
                                                    <af-items>
                                                        <PeerAf-list>
                                                            <type>ipv4-ucast</type>
                                                            <allowedSelfAsCnt>0</allowedSelfAsCnt>
                                                            <asOverride>disabled</asOverride>
                                                            <defOrg>disabled</defOrg>
                                                            <encapMpls>disabled</encapMpls>
                                                            <name>bgp-PeerAf</name>
                                                            <nhThirdparty>enabled</nhThirdparty>
                                                            <rewriteRtAsn>disabled</rewriteRtAsn>
                                                            <sendComExt>disabled</sendComExt>
                                                            <sendComStd>disabled</sendComStd>
                                                        </PeerAf-list>
                                                    </af-items>
                                                    <asn>65002</asn>
                                                </PeerCont-list>
                                            </peercont-items>
                                            <rtrId>3.3.3.3</rtrId>
                                        </Dom-list>
                                    </dom-items>
                                    <name>bgp-Inst</name>
                                </inst-items>
                                <name>bgp</name>
                            </bgp-items>
                        </System>
                    </config>
                    '''

                reply = m.edit_config(rpc, target='running')
                print(reply)
        
    

The Full Script

At this point, your script should look as follows:

        
        # Imports required
        from ncclient import manager
        from ydk.services import NetconfService, Datastore, CRUDService
        from ydk.providers import NetconfServiceProvider
        from ydk.path import Repository
        from ydk.models.openconfig import openconfig_bgp
        from ydk.models.openconfig import openconfig_bgp_types as oc_bgp_types

        # Define the path of the YANG models that we want to use.
        repo = Repository('/workspace/yang/vendor/cisco/nx/7.0-3-I7-3')

        # Mapping of device names to IPs
        duts = {
            "nx": '10.2.100.13',
        }

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

        # Instantiate the NetconfServiceProvider.
        provider = NetconfServiceProvider(
            repo=repo,
            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)

        # Create the netconf service.
        netconf = NetconfService()

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

        # Global configuration
        bgp.global_.config.as_ = 65003
        bgp.global_.config.router_id = '3.3.3.3'

        # Peer group configuration
        peer_group = bgp.peer_groups.PeerGroup()
        peer_group.peer_group_name = "EBGP-XRV"
        peer_group.config.peer_group_name = "EBGP-XRV"
        peer_group.config.peer_as = 65002
        bgp.peer_groups.peer_group.append(peer_group)

        # Add the XRv neighbor
        neighbor = bgp.neighbors.Neighbor()
        neighbor.neighbor_address = "10.2.2.2"
        neighbor.config.neighbor_address = "10.2.2.2"
        neighbor.config.peer_group = "EBGP-XRV"
        bgp.neighbors.neighbor.append(neighbor)

        # Commit the changes to the running data-store.
        netconf.edit_config(provider, Datastore.running, bgp)

        # Lastly, add address-family ipv4 unicast to the peer template
        # since oc-bgp-types:IPV4_UNICAST is unsupported by NXv
        with manager.connect(host=ip, port="830", username="admin",
                     password="cisco.123", hostkey_verify=False,
                     device_params={"name": "nexus"},
                     look_for_keys=False, allow_agent=False) as m:

            # RPC call to add BGP address-family to peer group
            rpc = '''
                    <config>
                        <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                        <bgp-items>
                            <inst-items>
                                <adminSt>enabled</adminSt>
                                <asn>65003</asn>
                                <dom-items>
                                    <Dom-list>
                                        <name>default</name>
                                        <peercont-items>
                                            <PeerCont-list>
                                                <name>eBGP-XR</name>
                                                <adminSt>enabled</adminSt>
                                                <af-items>
                                                    <PeerAf-list>
                                                        <type>ipv4-ucast</type>
                                                        <allowedSelfAsCnt>0</allowedSelfAsCnt>
                                                        <asOverride>disabled</asOverride>
                                                        <defOrg>disabled</defOrg>
                                                        <encapMpls>disabled</encapMpls>
                                                        <name>bgp-PeerAf</name>
                                                        <nhThirdparty>enabled</nhThirdparty>
                                                        <rewriteRtAsn>disabled</rewriteRtAsn>
                                                        <sendComExt>disabled</sendComExt>
                                                        <sendComStd>disabled</sendComStd>
                                                    </PeerAf-list>
                                                </af-items>
                                                <asn>65002</asn>
                                            </PeerCont-list>
                                        </peercont-items>
                                        <rtrId>3.3.3.3</rtrId>
                                    </Dom-list>
                                </dom-items>
                                <name>bgp-Inst</name>
                            </inst-items>
                            <name>bgp</name>
                        </bgp-items>
                    </System>
                </config>
                '''

            reply = m.edit_config(rpc, target='running')
            print(reply)
        
    

Execute Python Script

Remember to save your new script with Ctrl+s, then return to your container in Terminal. Execute your new script that will provision BGP to your NX device.

        
            cd /workspace/
            python3.6 ydk_nx_bgp_conf.py
        
    

Verify BGP Configuration on NX

You can log back into your NX device using nx and issue show run bgp to verify the configuration pushed using the YDK library and NETCONF:

Issue in container:

        
            nx
        
    

Issue on NX device:

        
            show run bgp
        
    

Your output should look similar to the below:

        
            Pod00-N9Kv# show run bgp

            !Command: show running-config bgp
            !Time: Tue Jun 12 10:53:38 2018

            version 7.0(3)I7(3)
            feature bgp

            router bgp 65003
              router-id 3.3.3.3
              template peer eBGP-XR
                remote-as 65002
                address-family ipv4 unicast
              neighbor 10.2.2.2
                inherit peer eBGP-XR


            Pod00-N9Kv#
        
    

Footnote for oc-bgp-types:IPV4_UNICAST

During the course of the lab, it was discovered that the version of NX used doesn't support the oc-bgp-types:IPV4_UNICAST value. This is currently a noted deviation ("not-supported") for NX and OpenConfig. Per the Nexus BGP Configuration Guide, "You must configure an address family under a neighbor for the BGP session establishment." Because of this, the lab used the NX native YANG model to add the IPv4 Unicast address family under our peer template.

If you were to attempt to supply a oc-bgp-types:IPV4_UNICAST() as was done in the XR example, you would have encountered the following error:

        
        <rpc-error>
            <error-type>protocol</error-type>
            <error-tag>operation-not-supported</error-tag>
            <error-severity>error</error-severity>
            <error-message xml:lang="en">Property 'afi-safi-name' value oc-bgp-types:IPV4_UNICAST is unsupported</error-message>
            <error-path>/bgp/peer-groups/peer-group[]/afi-safis/afi-safi[]</error-path>
        </rpc-error>
        
    

Move on to configuring the final device with BGP in your topology, the CSRv / IOS-XE router.