cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
79049
Views
89
Helpful
40
Comments
aradford
Cisco Employee
Cisco Employee

Introduction

 

Cisco has recently introduced NETCONF/YANG support across the enterprise network portfolio.  This capability is available in the 16.3 XE code for routers and switches.  NETCONF/YANG allows programmatic access to network devices using structured data.

 

I have had a number of questions from customers looking to get started with NETCONF/YANG, which I will address here.

 

To keep it practical, we will end up using NETCONF/YANG to programmatically change the vlan membership for a switch interface.

 

Getting Started

 

Many of you will know that Cisco has had IOS support for NETCONF since 2004.  The 2004 version did not really have a data model and was just using NETCONF for transport.  You still needed to send/receive CLI chunks as text, which was similar to just SSH-ing into the device and running commands directly. (NOTE: technically there were a few small differences, but I am not going into them here J)

 

The 2016 version of NETCONF uses YANG to structure the data that is sent and received, which makes is much simpler to work with programatically.  In the past, you would need to "screen scrape" CLI with regular expressions to extract specific pieces of information.

 

To enable the NETCONF, a single command is required.  These examples are using a 3850 switch running 16.3.2 code.

3850-remote#conf t

Enter configuration commands, one per line. End with CNTL/Z.  

3850-remote(config)#netconf-yang

 

 

Testing the transport

 

NETCONF uses SSH as a transport, so one of the simplest ways to see how it works is to SSH to the device. I can simply use the ssh command and connect to port 830 (the default NETCONF port).

$ ssh -p 830 sdn@10.10.6.2

sdn@10.10.6.2's password:

 

Enter a password and there will be a large slab of XML in response.   This has been <SNIPPED/> for brevity

<?xml version="1.0" encoding="UTF-8"?>

<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">

<capabilities>

<capability>urn:ietf:params:netconf:base:1.0</capability>

<capability>urn:ietf:params:netconf:base:1.1</capability>

<capability>urn:ietf:params:netconf:capability:writable-running:1.0</capability>

<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>

<capability>urn:ietf:params:netconf:capability:validate:1.0</capability>

<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>

<capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>

<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>

<capability>urn:ietf:params:netconf:capability:interleave:1.0</capability>

<capability>http://tail-f.com/ns/netconf/actions/1.0</capability>

<capability>http://tail-f.com/ns/netconf/extensions</capability>

<capability>urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=report-all</capability>

<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?revision=2011-06-01&amp;module=ietf-netconf-with-defaults</capability>

<capability>http://cisco.com/ns/yang/ned/ios?module=ned&revision=2016-09-19&deviations=ned-switching-devs</capability>

16</capability>

<SNIPPED/>

<capability>urn:ietf:params:xml:ns:yang:smiv2:VPN-TC-STD-MIB?module=VPN-TC-STD-MIB&amp;revision=2005-11-15</capability>

</capabilities>

<session-id>19150</session-id></hello>]]>]]>

 

 

The key point here is that this is a <hello> message from the NETCONF device, containing a list of <capabilities>.  The capabilities contain all of the YANG models that the device supports.

 

NOTE: the delimiter string ]]>]]> at the end of the response signifies the end of the message. You would need to use this string to indicate the end of any message you send back.

 

To continue the session, you would need to respond to the hello with a list of capabilities that you support.  The simplest response is below.  Remember the delimiter string (]]>]]>).

 

<?xml version="1.0" encoding="UTF-8"?>

<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">

<capabilities>

  <capability>urn:ietf:params:netconf:base:1.0</capability>

</capabilities>

  </hello>]]>]]>

 

 

You will not see a response, which is expected.  You can now send NETCONF commands.  The following example will return the running configuration.

 

<?xml version="1.0"?>

<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"

message-id="101">

<get-config>

<source>

<running/>

</source>

</get-config>

</rpc>]]>]]>

 

 

You will get an XML response that contains the full configuration.

 

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><native xmlns="http://cisco.com/ns/yang/ned/ios"><device-model-version><major>2</major><minor>1</minor><bug-fix>0</bug-fix></device-model-version><version>16.3</version><boot><system><switch><all><flash>packages.conf</flash></all></switch></system></boot><service><password-recovery>false</password-recovery><timestamps><debug><datetime><msec/></datetime></debug><log><datetime><msec/></datetime></log></timestamps><compress-config/></service><hostname>3850-remote</hostname>

<SNIPPED/>

 

 

You can pass this through an XML formatter to get a structured view:

 

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">

    <data>

        <native xmlns="http://cisco.com/ns/yang/ned/ios">

            <device-model-version>

                <major>2

                </major>

                <minor>1

                </minor>

                <bug-fix>0

                </bug-fix>

            </device-model-version>

            <version>16.3

            </version>

            <boot>

                <system>

                    <switch>

                        <all>

                            <flash>packages.conf         

                            </flash>

                        </all>

                    </switch>

                </system>

            </boot>

            <service>

                <password-recovery>false

                </password-recovery>

                <timestamps>

                    <debug>

                        <datetime>

                            <msec/>

                        </datetime>

                    </debug>

                    <log>

                        <datetime>

                            <msec/>

                        </datetime>

                    </log>

                </timestamps>

                <compress-config/>

            </service>

            <hostname>3850-remote

            </hostname>

<SNIPPED/>

 

 

Using a NETCONF tool

 

There are a number of tools that simplify interacting with NETCONF.  One of my favourites (as it still exposes the underlying semantics of the protocol, which I think is important when learning) is netconf-console. You can download it from https://github.com/OpenNetworkingFoundation/configuration/tree/master/netconf-console

 

The file was renamed to netconf-console.py.  In the examples below, an explicit host, username, password and the netconf port were specified.  You can edit the netconf-console.py file to change the default username, password and port to make testing easier.

 

The operation that is being run here is just "hello", so will return a list of capabilities from the device. This will be identical to the first step in the earlier SSH example.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830

  --hello

 

 

Similar to the earlier example, the get-config command can be used, and this time the XML response will be pretty-printed.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <native xmlns="http://cisco.com/ns/yang/ned/ios">

      <device-model-version>

<major>2</major>

<minor>1</minor>

<bug-fix>0</bug-fix>

      </device-model-version>

<version>16.3</version>

      <boot>

        <system>

          <switch>

            <all>

<flash>packages.conf</flash>

            </all>

          </switch>

        </system>

      </boot>

<SNIPPED/>

 

 

Why Models

The earlier NETCONF responses were XML.  This is known as structured data.  The reason this is important is it is very easy for software to parse and understand. XML tags are used to mark-up or describe the data they contain.   For example <major>2</major> denotes an attribute "major" with a value of 2.  NOTE <tag> is the start and </tag> is the end.

 

XML is also hierarchical, <major>2</major> is contained in the <device-model-version> tag.  We know that the device-model-version/major is 2.  Other attributes of device-model-version include minor and bug-fix.

trees.png

 

 

 

Software engineers like hierarchical trees.  These are easy to search, sort and scale.  XML tags define a tree-like structure, with a root, branches and leaves. In the example above <major>2</major> is a leaf as it only contains data, not other XML constructs.  YANG can be mapped easily into XML.  We will cover YANG in a future blog.

 

As seen in the earlier example, a full device configuration may be quite large.  In my case it was nearly 3000 lines.  Some of this is due to the overhead of XML, but it is still a lot of data.  Here is where trees/XML can help due to filtering.

 

A filter can be used to specify a subset of the configuration.  For example, if you are only interested in the configuration of the interfaces on the device. This is achieved with the -x "interfaces" option.  "-x" means XPATH for those familiar with XML.

 

This time only the interface specific configuration is returned (386 lines, vs 3000 lines for full configuration for my example).

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config -x "interfaces"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">

        <interface>

        <name>GigabitEthernet1/0/1</name>

        <description>uplink to router</description>

        <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>

        <enabled>true</enabled>

        <diffserv-target-entry xmlns="urn:ietf:params:xml:ns:yang:ietf-diffserv-target">

<direction>outbound</direction>

<policy-name>EasyQos-Egress</policy-name>

        </diffserv-target-entry>

        <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">

          <address>

            <ip>10.10.6.2</ip>

<netmask>255.255.255.0</netmask>

          </address>

        </ipv4>

        <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/>

      </interface>

      <interface>

        <name>GigabitEthernet1/0/10</name>

        <description>802.1x host</description>

 

<SNIPPED/>

 

 

Filters can be used to drill down as deep as you like.  In this example, only the configuration of interface GigabitEthernet1/0/1 is returned. The following filter interfaces/interface[name='GigabitEthernet1/0/1'] achieves this.

 

"name" is a key, and has a value of "GigabitEthernet1/0/1" [name='GigabitEthernet1/0/1']. NOTE: the name is case sensitive.

 

For the first time I do not need to truncate the output.

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config -x "interfaces/interface[name='GigabitEthernet1/0/1']"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">

      <interface>

        <name>GigabitEthernet1/0/1</name>

        <description>uplink to router</description>

        <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>

        <enabled>true</enabled>

        <diffserv-target-entry xmlns="urn:ietf:params:xml:ns:yang:ietf-diffserv-target">

          <direction>outbound</direction>

<policy-name>EasyQos-Egress</policy-name>

        </diffserv-target-entry>

        <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">

          <address>

            <ip>10.10.6.2</ip>

            <netmask>255.255.255.0</netmask>

          </address>

        </ipv4>

        <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/>

      </interface>

    </interfaces>

  </data>

</rpc-reply>>

 

 

This example takes this one step further by just returning the description attribute for a specific interface.  NOTE: the "name" attribute is also returned, as it is the key (details on why this is the case will be explained in a future blog).

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config -x "interfaces/interface[name='GigabitEthernet1/0/1']/description"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">

      <interface>

        <name>GigabitEthernet1/0/1</name>

        <description>uplink to router</description>

      </interface>

    </interfaces>

  </data>

</rpc-reply>

 

 

Another example of the power of filters is restricting to a set of leaves.  In this example the search is restricted to get the description for all interfaces.  Only interfaces with descriptions are returned.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config -x "interfaces/interface/description"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">

      <interface>

        <name>GigabitEthernet1/0/1</name>

        <description>uplink to router</description>

      </interface>

      <interface>

        <name>GigabitEthernet1/0/10</name>

        <description>802.1x host</description>

      </interface>

      <interface>

        <name>GigabitEthernet1/0/4</name>

        <description>IP Phone test</description>

      </interface>

    </interfaces>

  </data>

</rpc-reply>

 

This is a great example of why structured data (models) is important.  If I was to try to do this from CLI, I could use the following, but i still need to filter/regexp the relevant information:

3850-remote#show run  | inc Gigabit|desc 

  description Topology control

  description DHCP snooping, show forward and rest of traffic

  description Learning cache ovfl, Crypto Control, Exception, EGR Exception, NFL SAMPLED DATA, Gold Pkt, RPF Failed

  description Wireless priority 1

  description Wireless priority 2

  description Wireless priority 3,4 and 5

  description Routing control

  description Protocol snooping

interface GigabitEthernet0/0

interface GigabitEthernet1/0/1

description uplink to router

interface GigabitEthernet1/0/2

interface GigabitEthernet1/0/3

interface GigabitEthernet1/0/4

description IP Phone test

interface GigabitEthernet1/0/5

interface GigabitEthernet1/0/6

interface GigabitEthernet1/0/7

interface GigabitEthernet1/0/8

interface GigabitEthernet1/0/9

interface GigabitEthernet1/0/10

description 802.1x host

<SNIPPED/>

How to configure a VLAN?

Notice there is no configuration for vlan membership in the example above.  We are using the standard ietf-interfaces model, and this does not (currently) include vlan membership.

 

More advanced features are modelled in the "native" model.  The native model interface structure is slightly different to the standard model.  The interface name is very different.  Instead of the name being "GigabitEthernet0/0" there is a container for "GigabitEthernet" and the name is simply "0/0".

 

To get all interfaces, the filter is just "native/interface"  instead of "interfaces/interface" with the standard model.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p sdn123 --port 830 --get-config -x "native/interface"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <native xmlns="http://cisco.com/ns/yang/ned/ios">

      <interface>

        <GigabitEthernet>

          <name>0/0</name>

          <negotiation>

            <auto>true</auto>

          </negotiation>

          <vrf>

<forwarding>Mgmt-vrf</forwarding>

          </vrf>

          <ip>

            <no-address>

<address>false</address>

            </no-address>

          </ip>

        </GigabitEthernet>

        <GigabitEthernet>

<SNIPPED/>

 

 

To get the configuration of a specific interface 1/0/10:

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p sdn123 --port 830 --get-config  -x "native/interface/GigabitEthernet[name='1/0/10']"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <native xmlns="http://cisco.com/ns/yang/ned/ios">

      <interface>

        <GigabitEthernet>

          <name>1/0/10</name>

          <switchport>

            <access>

              <vlan>

                <vlan>30</vlan>

              </vlan>

            </access>

            <mode>

              <access/>

            </mode>

          </switchport>

          <authentication>

<host-mode>multi-auth</host-mode>

          </authentication>

          <spanning-tree>

            <portfast/>

          </spanning-tree>

          <description>802.1x host</description>

          <service-policy>

<output>EasyQos-Egress</output>

          </service-policy>

</GigabitEthernet>

      </interface>

    </native>

  </data>

</rpc-reply>

 

  Looking at interface 1/0/9, there is currently no vlan defined.

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p sdn123 --port 830 --get-config  -x "native/interface/GigabitEthernet[name='1/0/9']"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <native xmlns="http://cisco.com/ns/yang/ned/ios">

      <interface>

        <GigabitEthernet>

          <name>1/0/9</name>

          <switchport>

            <mode>

              <access/>

            </mode>

          </switchport>

          <service-policy>

<output>EasyQos-Egress</output>

          </service-policy>

        </GigabitEthernet>

      </interface>

    </native>

  </data>

</rpc-reply>  </data>

  </rpc-reply>

 

 

I create a file /tmp/edit with the changes I want to make to interface 1/0/9.  I want to change the access vlan to vlan 30.

 

 

$ cat /tmp/edit

<native xmlns="http://cisco.com/ns/yang/ned/ios">

      <interface>

        <GigabitEthernet>

          <name>1/0/9</name>

          <switchport>

            <access>

              <vlan>

                <vlan>30</vlan>

              </vlan>

            </access>

          </switchport>

        </GigabitEthernet>

      </interface>

    </native>

 

 

This change can be applied by using the edit-config RPC.  It returns successfully.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --edit-config=/tmp/edit

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <ok/>

</rpc-reply>

 

 

I can now verify the switchport/access/vlan configuration for interface 1/0/9.  It has been changed to vlan 30.

 

$ ./netconf-console.py --host=10.10.6.2 -u sdn -p password --port 830 --get-config  -x "native/interface/GigabitEthernet[name='1/0/9']/switchport/access/vlan"

<?xml version="1.0" encoding="UTF-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">

  <data>

    <native xmlns="http://cisco.com/ns/yang/ned/ios">

      <interface>

        <GigabitEthernet>

          <name>1/0/9</name>

          <switchport>

            <access>

              <vlan>

                <vlan>30</vlan>

              </vlan>

            </access>

          </switchport>

        </GigabitEthernet>

      </interface>

    </native>

  </data>

</rpc-reply>

 

 

Summary

This blog post has shown some basic ways to interact with NETCONF/YANG in Cisco IOS-XE 16.3.2.  The following concepts have been explored:

  • - SSH and netconf-console to interact with a network device
  • - Model driven data access with XPATH filters
  • - "get-config" and "edit-config" RPC calls.
  • - IETF standard and "native" configuration models

 

What Next?

 

In the meantime, if you would like to learn more about this, you could come hang out with us in The Cisco Devnet DNA Community.  We’ll have a continuous stream of blogs like this and you can ask questions and we’ll get you answers.

 

Future blogs will contain more details of NETCONF/YANG, operational vs configuration data and other tools/python libraries you can use to interface via NETCONF.  Check out  Getting Started with NETCONF/YANG – Part 2

 

Thanks for reading,

@adamradford123

40 Comments
jboga
Level 5
Level 5

Adam,

Amazing blog post. Very clear and with lots of interesting information. I love the netconf-console recommendation.

My starting point in the learning curve of NETCONF/RESTCONF were the DevNet learning labs and also the

Programming and Automating Cisco Networks: A guide to network programmability and automation in the data center, campus, and WAN book. I highly recommend both resources.

aradford
Cisco Employee
Cisco Employee

Thanks Jose,

Those look like great resources too.  I appreciate you pointing them out.

Another blog about to go up from me with a new tool.

aradford
Cisco Employee
Cisco Employee

Hi Jose, check out Getting Started with NETCONF/YANG – Part 2 there is a tool called ncc which you might like.  Templates, snippets etc

amguns101
Level 1
Level 1

Hi adam, is APIC-EM based on OpenDaylight? to see if we deal with the same MD-SAL...

thank you

aradford
Cisco Employee
Cisco Employee

Hi Ben,  no APIC-EM is not based on MD-SAL.

amguns101
Level 1
Level 1

Thanks adam,

More couple of questions:

So how YANG data models are defined in APIC-EM? services are not based on YANG models?

can we load YANG models to the controller for new services?

Any information in that direction?

aradford
Cisco Employee
Cisco Employee

Hi Ben,

this blog series is about using YANG to connect directly to devices.

I will cover the APIC-EM implications of this later on.

Nothing publicly  available right now.

amguns101
Level 1
Level 1

Hi Adam,

You are covering YANG in this blog & also APIC-EM in another blog, and as soon as i'm working on APIC-EM with students right now, i asked the question on YANG on APIC-EM.

I will wait for any updates about YANG in APIC-EM.

Thanks a lot Adam.

hawsecroft
Level 1
Level 1

Adam,

Thanks for posting this. Good material to bring the layman up to speed with the technology.

Looking forward to seeing you speak at Cisco Live in Berlin this month.

Thanks,

Dan

crhill@cisco.com
Cisco Employee
Cisco Employee

Hi Adam,

Great info as always from you Mate, and anyone starting to work with interaction with YANG models for device configuration, this is an awesome write-up.  Quick question, can you make a few recommendations as it relates to your XML interpreter you use?  I have seen this done through python scripts as well as versions of NETCONF clients (using filtering capabilities), but always looking for better and easier tools.  While using your method above will work fine, there are some instances where I just want to abstract the XML view of certain config blocks within a router/switch, and that interpreter would be useful.

Thanks,

Craig

aradford
Cisco Employee
Cisco Employee

Thanks Dan,  be good to catch up at live

aradford
Cisco Employee
Cisco Employee

Hi Craig, did you take a look at ncc.py in the next blog?

That supports snippets and snippets with variables (like interface and vlan) in the above example.

In addition there is a web based tool i will introduce in the upcoming blogs 3

crhill@cisco.com
Cisco Employee
Cisco Employee

Thanks Adam, ncc.py is exactly what I was looking for!

Thanks Mate and see you next month down under. :-)

Craig

aradford
Cisco Employee
Cisco Employee

Awesome... hopefully see you in Berlin @ CLEU?  BTW, you will quite like the flask-netconf tool too... Blog3 for that.

aradford
Cisco Employee
Cisco Employee

BTW, if you have other suggestions for ncc, let us know.  we are still working on improving it.

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: