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

Get Interfaces Configured by Ansible

The previous section jumped right in making a configuration change to your devices in your network using the Ansible framework and NETCONF's edit-config operation. As mentioned in the overview, NETCONF has get and get-config operations. For these operations, NETCONF has a mechanism mentioned in RFC 6241 for filtering exactly what configuration sections you wish to get back. This is called subtree filtering. Subtree filtering can be done on:

  • Namespaces
  • Match Attribute Expressions
  • Containment Nodes
  • Selection Nodes
  • Content Match Nodes

In the sections below, you will use get-config with a combination of subtree filtering on namespaces and content matching on nodes.

Normally, a subtree filter would have to have outer <filter></filter> elements with the type within the element heading equal to subtree; which would look something like this:

            
                <filter type="subtree">
                    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>
                </filter>
            
        
However, using the Pythonic approach with ncclient and its get_config() method, has a filter argument where you simply specify the filter type, in this case subtree, along with the XML RPC that you want to get a configuration rpc-reply. In the sections of code that you work with below, this will look something like this:
            
                r = m.get_config('running', filter=('subtree', int_filter))
            
        
Remember, you always must specify the configuration datastore that you want to query as well, such as done above.

There will be a netconf_get module coming in the near future that performs the NETCONF get and/or get-config operations in Ansible 2.6.

This section of the lab is meant to show you how to get the YANG configuration data structure from an already configured device. This serves to allow you to not have to sift through YANG data models if you so choose (although we recommended you do spend some time doing this) and also teaches you to pull config to build config templates or verify config.

Get XE Interface Configured by Ansible

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

        
            ncclient_xe_get_int_config.py
        
    

The code below is very similar from the previous file you created using the ncclient manager where you got each device's capabilities. You will then see a variable called int_filter that is set to hold a string XML RPC. The triple quote in Python is used here to expand the string multiple lines. The XML could have been on a single line with normal single or double quotes, but that doesn't make it very human readable. This filter, as you might be able to tell, uses the IETF interfaces definition, as was used for the configuration. You could also use the native XE definition.

Then, as mentioned above, you will use ncclient's get_config as a method to your instantiated manager object. The CSRv/XE, at the moment, only supports the running datastore, so you must pass "running" as a parameter, then the filter argument function where you specify the filter type parameter as subtree and then finally, the actual XML-based filter using the filter variable.

Look over and copy the code below into you newly created Python file. Remember to save your code with Ctrl+s.

        
            from ncclient import manager


            def main():
                """
                Main method that prints netconf capabilities of device.
                """

                # Device dictionary that provides key/value connection information
                device = {"ip": "10.2.100.11", "port": "830", "platform": "csr",}

                # ncclient manager instantiation for csr
                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:

                    # Filter using top-level container namespace and node matching
                    int_filter = '''
                                <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
                                    <interface>
                                        <name>GigabitEthernet2</name>
                                    </interface>
                                </interfaces>
                                '''

                    # get-config RPC against the running datastore using a subtree filter
                    reply = m.get_config('running', filter=('subtree', int_filter))

                    # Print RPC reply
                    print(reply)


            if __name__ == '__main__':
                main()

        
    

Return to your container in your Terminal to execute your Python script.

        
            cd /workspace/
            python3.6 ncclient_xe_get_int_config.py
        
    

In the RPC reply, you will notice the XML elements contain the configured interface description and IP address for the interface that you filtered on. This is what was configured by via Ansible using the IETF definition.

As you can see this is a way to get the XML configuration of an already configured device to potentially build configuration templates.

        
            [root@25ef0c91db80 workspace]# python3.6 ncclient_xe_get_int_config.py
            <?xml version="1.0" encoding="UTF-8"?>
            <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:f6bd89d8-5bf9-42f3-aa05-a66966b1f450"
            xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface><name xmlns:nc='urn:ietf:params:xml:ns:netconf:base:1.0'>GigabitEthernet2</name><description>
            Configured by Ansible NETCONF</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">
            ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address>
            <ip>10.1.1.1</ip><netmask>255.255.255.252</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
            </ipv6></interface></interfaces></data></rpc-reply>
            [root@25ef0c91db80 workspace]#
        
    

This is like show run from the CLI, but is structured data that can be manipulated, templatized, etc. Feel free to login to your xe device if you want to check the CLI using show run interface GigabitEthernet2 to compare.

        
            Pod00-CSRv#show run interface gigabitEthernet2
            Building configuration...

            Current configuration : 162 bytes
            !
            interface GigabitEthernet2
             description Configured by Ansible NETCONF
             ip address 10.1.1.1 255.255.255.252
             negotiation auto
             no mop enabled
             no mop sysid
            end

            Pod00-CSRv#
        
    

Get XR Interface Configured by Ansible

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

        
            ncclient_xr_get_int_config.py
        
    

Following the same approach as you did for the XE platform above, you will use the same variable called int_filter that is set to hold a string XML RPC. This time, the filter uses the OpenConfig definition, just as it did for the initial configuration via Ansible NETCONF. Again, you could use the native XR definition.

Ncclient's get_config manager object method will be used the same as above, except this time, you will call it twice. The reason for this, is that XR platforms support both the running and candidate datastore. If you're familiar with XR, then you know that for a configuration change to occur to the running config, then a commit must occur, which is symbolic to the candidate datastore. So, for your XRv, the code below will pass the string "running" as a parameter, then the filter argument function where you specify the filter type parameter as subtree and the then the actual XML-based filter using the filter variable. This is repeated in setting the datastore parameter to the string "candidate".

Look over and copy the code below into you newly created Python file. Remember to save your code with Ctrl+s.

        
            from ncclient import manager


            def main():
                """
                Main method that prints netconf capabilities of device.
                """

                # Device dictionary that provides key/value connection information
                device = {"ip": "10.2.100.12", "port": "830", "platform": "iosxr",}

                # ncclient manager instantiation for iosxr
                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:

                    # Filter using top-level container namespace and node matching
                    int_filter = '''
                                <interfaces xmlns="http://openconfig.net/yang/interfaces">
                                    <interface>
                                        <name>GigabitEthernet0/0/0/0</name>
                                    </interface>
                                </interfaces>
                                '''

                    # get-config RPC against the running datastore using a subtree filter
                    reply_running = m.get_config('running', filter=('subtree', int_filter))

                    # Print RPC reply against running datastore
                    print(reply_running)

                    print('')

                    # get-config RPC against the candidate datastore using a subtree filter
                    reply_candidate = m.get_config('candidate', filter=('subtree', int_filter))

                    # Print RPC reply against candidate datastore
                    print(reply_candidate)


            if __name__ == '__main__':
                main()

        
    

Return to your container in your Terminal to execute your Python script.

        
            cd /workspace/
            python3.6 ncclient_xr_get_int_config.py
        
    

In the RPC reply, you will notice the XML elements contain the configured interface description and IP address for the interface that you filtered on. This is what was configured by via Ansible using the OpenConfig definition. The running and candidate datastores should be displayed and look the same.

        
            [root@25ef0c91db80 workspace]# python3.6 ncclient_xr_get_int_config.py
            <?xml version="1.0"?>
            <rpc-reply message-id="urn:uuid:f01fd3d2-bfc3-4c02-9a04-813650ecb36d" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
             <data>
              <interfaces xmlns="http://openconfig.net/yang/interfaces">
               <interface>
                <name>GigabitEthernet0/0/0/0</name>
                <config>
                 <name>GigabitEthernet0/0/0/0</name>
                 <type xmlns:idx="urn:ietf:params:xml:ns:yang:iana-if-type">idx:ethernetCsmacd</type>
                 <enabled>true</enabled>
                 <description>Configured by Ansible NETCONF</description>
                </config>
                <ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet">
                 <config>
                  <auto-negotiate>false</auto-negotiate>
                 </config>
                </ethernet>
                <subinterfaces>
                 <subinterface>
                  <index>0</index>
                  <ipv4 xmlns="http://openconfig.net/yang/interfaces/ip">
                   <addresses>
                    <address>
                     <ip>10.1.1.2</ip>
                     <config>
                      <ip>10.1.1.2</ip>
                      <prefix-length>30</prefix-length>
                     </config>
                    </address>
                   </addresses>
                  </ipv4>
                 </subinterface>
                </subinterfaces>
               </interface>
              </interfaces>
             </data>
            </rpc-reply>


            <?xml version="1.0"?>
            <rpc-reply message-id="urn:uuid:77bec844-e7ae-4c4c-8809-2b99bff7807d" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
             <data>
              <interfaces xmlns="http://openconfig.net/yang/interfaces">
               <interface>
                <name>GigabitEthernet0/0/0/0</name>
                <config>
                 <name>GigabitEthernet0/0/0/0</name>
                 <type xmlns:idx="urn:ietf:params:xml:ns:yang:iana-if-type">idx:ethernetCsmacd</type>
                 <enabled>true<<enabled>
                 <description>Configured by Ansible NETCONF</description>
                </config>
                <ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet">
                 <config>
                  <auto-negotiate>false</auto-negotiate>
                 </config>
                </ethernet>
                <subinterfaces>
                 <subinterface>
                  <index>0</index>
                  <ipv4 xmlns="http://openconfig.net/yang/interfaces/ip">
                   <addresses>
                    <address>
                     <ip>10.1.1.2</ip>
                     <config>
                      <ip>10.1.1.2</ip>
                      <prefix-length>30</prefix-length>
                     </config>
                    </address>
                   </addresses>
                  </ipv4>
                 </subinterface>
                </subinterfaces>
               </interface>
              </interfaces>
             </data>
            </pc-reply>

            [root@25ef0c91db80 workspace]#
        
    

This is like show run from the CLI, but is structured data that can be manipulated, templatized, etc. Feel free to login to your xr device if you want to check the CLI using show run interface GigabitEthernet0/0/0/0 and show run interface GigabitEthernet0/0/0/1 to compare.

        
            RP/0/RP0/CPU0:Pod00-XRv#show run interface gigabitEthernet 0/0/0/0
            Mon May 21 17:36:29.407 UTC
            interface GigabitEthernet0/0/0/0
             description Configured by Ansible NETCONF
             ipv4 address 10.1.1.2 255.255.255.252
            !

            RP/0/RP0/CPU0:Pod00-XRv#
            RP/0/RP0/CPU0:Pod00-XRv#show run interface gigabitEthernet 0/0/0/1
            Mon May 21 17:36:29.407 UTC
            interface GigabitEthernet0/0/0/1
             description Configured by Ansible NETCONF
             ipv4 address 10.2.2.2 255.255.255.252
            !

            RP/0/RP0/CPU0:Pod00-XRv#
        
    

Get NX Interface Configured by Ansible

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

        
            ncclient_nx_get_int_config.py
        
    

For your NX platform, you will follow the same approach as the previous two scripts. However, this time, you will make two RPC filters and calls due to how the NX native definitions are modeled (remember these are derived from the NX-API REST object model). The first filter is used to get the physical interface's related elements, such as the description or state. The second filter is used to get the IPv4 logical elements. Like XE, NX only supports the running datastore, thus that will used for the datastore parameter.

Look over and copy the code below into you newly created Python file. Remember to save your code with Ctrl+s.

        
            from ncclient import manager


            def main():
                """
                Main method that prints netconf capabilities of device.
                """

                # Device dictionary to use
                device = {"ip": "10.2.100.13", "port": "830", "platform": "nexus",}

                # ncclient manager instantiation for 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:

                    # Filter using top-level container namespace and node matching for physical interface
                    int_filter1 = '''
                                <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                                    <intf-items>
                                        <phys-items>
                                            <PhysIf-list>
                                                <id>eth1/1</id>
                                            </PhysIf-list>
                                        </phys-items>
                                    </intf-items>
                                </System>
                                '''

                    # Filter using top-level container namespace and node matching for ipv4 interface
                    int_filter2 = '''
                                <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                                    <ipv4-items>
                                        <inst-items>
                                            <dom-items>
                                                <Dom-list>
                                                    <if-items>
                                                        <If-list>
                                                            <id>eth1/1</id>
                                                        </If-list>
                                                    </if-items>
                                                </Dom-list>
                                            </dom-items>
                                        </inst-items>
                                    </ipv4-items>
                                </System>
                                '''

                    # get-config RPC against the running datastore using first subtree filter
                    r = m.get_config('running', filter=('subtree', int_filter1))

                    # Print RPC reply against running datastore
                    print(r)

                    # get-config RPC against the running datastore using second subtree filter
                    r = m.get_config('running', filter=('subtree', int_filter2))

                    # Print RPC reply against running datastore
                    print(r)


            if __name__ == '__main__':
                main()
        
    
        
            cd /workspace/
            python3.6 ncclient_nx_get_int_config.py
        
    

In the RPC reply, you will notice the XML elements contain the configured interface description and IP address for the interface that you filtered on. This is what was configured by via Ansible using the Native definition.

        
            [root@25ef0c91db80 workspace]# python3.6 ncclient_nx_get_int_config.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:115f9127-68b8-4f91-9dff-59acf5ac11b7">
                <data>
                    <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                        <intf-items>
                            <phys-items>
                                <PhysIf-list>
                                    <id>eth1/1</id>
                                    <accessVlan>unknown</accessVlan>
                                    <adminSt>up</adminSt>
                                    <autoNeg>on</autoNeg>
                                    <beacon>off</beacon>
                                    <bw>0</bw>
                                    <delay>1</delay>
                                    <descr>Configured by Ansible NETCONF</descr>
                                    <dot1qEtherType>0x8100</dot1qEtherType>
                                    <duplex>auto</duplex>
                                    <eeep-items>
                                        <eeeLat>variable</eeeLat>
                                        <eeeLpi>aggressive</eeeLpi>
                                        <eeeState>not-applicable</eeeState>
                                    </eeep-items>
                                    <inhBw>4294967295</inhBw>
                                    <layer>Layer3</layer>
                                    <linkDebounce>100</linkDebounce>
                                    <linkDebounceLinkUp>0</linkDebounceLinkUp>
                                    <linkLog>default</linkLog>
                                    <loadp-items>
                                        <loadIntvl1>30</loadIntvl1>
                                        <loadIntvl2>300</loadIntvl2>
                                        <loadIntvl3>0</loadIntvl3>
                                    </loadp-items>
                                    <mdix>auto</mdix>
                                    <medium>broadcast</medium>
                                    <mode>access</mode>
                                    <mtu>1500</mtu>
                                    <nativeVlan>unknown</nativeVlan>
                                    <phys-items>
                                        <operLinkDebounce>100</operLinkDebounce>
                                    </phys-items>
                                    <physExtd-items>
                                        <allowMultiTag>disable</allowMultiTag>
                                        <bufferBoost>enable</bufferBoost>
                                        <routerMacIpv6Extract>disable</routerMacIpv6Extract>
                                        <stormCtrlBCastLevel>100.0</stormCtrlBCastLevel>
                                        <stormCtrlBCastPPS>4294967295</stormCtrlBCastPPS>
                                        <stormCtrlMCastLevel>100.0</stormCtrlMCastLevel>
                                        <stormCtrlMCastPPS>4294967295</stormCtrlMCastPPS>
                                        <stormCtrlUCastLevel>100.0</stormCtrlUCastLevel>
                                        <stormCtrlUCastPPS>4294967295</stormCtrlUCastPPS>
                                        <switchportVirtualEthernetBridge>disable</switchportVirtualEthernetBridge>
                                    </physExtd-items>
                                    <portT>unknown</portT>
                                    <priorflowctrl-items>
                                        <mode>0</mode>
                                    </priorflowctrl-items>
                                    <routerMac>not-applicable</routerMac>
                                    <rtvrfMbr-items>
                                        <tCl>l3Inst</tCl>
                                    </rtvrfMbr-items>
                                    <snmpTrapSt>enable</snmpTrapSt>
                                    <spanMode>not-a-span-dest</spanMode>
                                    <speed>auto</speed>
                                    <speedGroup>auto</speedGroup>
                                    <stormctrlp-items>
                                        <burstPps>4294967295</burstPps>
                                        <burstRate>100.0</burstRate>
                                        <rate>100.0</rate>
                                        <ratePps>4294967295</ratePps>
                                        <type>all</type>
                                    </stormctrlp-items>
                                    <trunkLog>default</trunkLog>
                                    <usage>discovery</usage>
                                    <userCfgdFlags/>
                                    <voicePortCos>-1</voicePortCos>
                                    <voicePortTrust>-1</voicePortTrust>
                                    <voiceVlanId>0</voiceVlanId>
                                    <voiceVlanType>none</voiceVlanType>
                                </PhysIf-list>
                            </phys-items>
                        </intf-items>
                    </System>
                </data>
            </rpc-reply>

            <?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:3d81737e-d4b2-4a34-827a-7c472e63a705">
                <data>
                    <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                        <ipv4-items>
                            <inst-items>
                                <dom-items>
                                    <Dom-list>
                                        <name>default</name>
                                        <if-items>
                                            <If-list>
                                                <id>eth1/1</id>
                                                <addr-items>
                                                    <Addr-list>
                                                        <addr>10.2.2.1/30</addr>
                                                        <pref>1</pref>
                                                        <tag>0</tag>
                                                        <type>primary</type>
                                                    </Addr-list>
                                                </addr-items>
                                                <adminSt>enabled</adminSt>
                                                <directedBroadcast>disabled</directedBroadcast>
                                                <forward>disabled</forward>
                                                <urpf>disabled</urpf>
                                            </If-list>
                                        </if-items>
                                    </Dom-list>
                                    <Dom-list>
                                        <name>management</name>
                                    </Dom-list>
                                </dom-items>
                            </inst-items>
                        </ipv4-items>
                    </System>
                </data>
            </rpc-reply>

            [root@25ef0c91db80 workspace]#

        
    

This is like show run from the CLI, but is structured data that can be manipulated, templatized, etc. Feel free to login to your xr device if you want to check the CLI using show run interface ethernet1/1 to compare. Further, you may have noticed above the significant amount of data you were returned for NX. This is similar to what you may be familiar with in show run interface ethernet1/1 all. You could get more granular with the filter if desired.

        
            Pod00-N9Kv# show run interface ethernet 1/1

            !Command: show running-config interface Ethernet1/1
            !Time: Mon May 21 17:35:46 2018

            version 7.0(3)I7(3)

            interface Ethernet1/1
              description Configured by Ansible NETCONF
              no switchport
              ip address 10.2.2.1/30
              no shutdown

            Pod00-N9Kv#
        
    

Continue to the next section where you will use ncclient for the edit-config operation, similar to how Ansible NETCONF uses it, but you will develop the raw Python code.