Skip navigation
All Places > DevNet > DNA > Blog > 2017 > January
2017

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.

 

Blog Getting Started with NETCONF/YANG – Part 1 covered some of the basics to get started with NETCONF/YANG.  It showed how to get access to device configuration as well as configuration changes.

 

Making things Easier – NCC

 

ncc (netconf client) is a tool we have been working on to make it a bit easier to get started learning about NETCONF/YANG.  You can download it from github at https://github.com/CiscoDevNet/ncc .

 

Getting Started with NETCONF/YANG – Part 2

 

To create a virtual environment for python and install the required packages, follow the instructions below.  If you are new to python and want to understand a little more about virtual environments, take a look at this article http://docs.python-guide.org/en/latest/dev/virtualenvs

 

This is assuming a LINUX or Mac. 

 

git clone https://github.com/CiscoDevNet/ncc.git

cd ncc

virtualenv v

. v/bin/activate

pip install --upgrade pip

pip install -r requirements.txt

 

 

For a Mac you might need the following two commands if you do not have pip and virtualenv installed.

 

sudo easy_install pip

sudo pip install virtualenv

 

 

You will need a device running IOS-XE 16.3.2 or greater for the examples below.  The following example just connects to the device and gets a list of capabilities

 

$ ./ncc.py --host 10.10.6.2 --username sdn --password password --capabilities

urn:cisco:params:xml:ns:yang:cisco-qos-common?module=cisco-qos-common&revision=2015-05-09

urn:cisco:params:xml:ns:yang:cisco-environment?module=cisco-environment&revision=2015-04-09

urn:cisco:params:xml:ns:yang:cisco-process-cpu?module=cisco-process-cpu&revision=2015-04-09

urn:cisco:params:xml:ns:yang:cisco-efp-stats?module=cisco-efp-stats&revision=2015-07-07

<SNIPPED>

 

 

Structured Data – YANG

 

One thing we skipped over last time was YANG and why structured is so important.   One question people often ask, is what is wrong with SNMP?  SNMP also provides structured data.   The SNMP Object ID (OID) mappings are structured but quite challenging to manage. For a start, it is quite difficult to tell what is operational data (statistics) vs configuration data.

 

snmp.png

 

 

YANG is much simpler and easier to understand.  Looking at the interfaces module, there are a couple of core concepts.

  • A container called "interfaces".  A container only has a single instance (which differentiates it from a list).  It can have multiple components, in this example there is just one ("interface").
  • A list called "interface".  There can be multiple elements in a list (as you would expect with an interface list).  A list requires a key to reference list members. In this case the key is a leaf called "name".
  • A leaf node is an attribute that holds data.  Examples below include "name" and "description" etc.

 

interface-yang.png

 

 

This module contains all of the configuration data for an interface.  In reality there are many more leaves as these modules can be augmented. There will be an augmentation example very soon.

 

Yang models can be mapped quite simply into xml payloads and searched using filters/xpath statements as seen in earlier blogs.    ncc.py contains some prebuilt filters to demonstrate this.   To get a list of ncc filters, you can run the following command.

 

$./ncc.py --host 10.10.6.2 --username sdn --password password --list-filters --snippets ./snippets-xe

Named filters:

  MIB-filter :{ "MIB" : "" }

  confd-state

  ietf-intf-named-description :{ "INTF_NAME" : "" }

  ietf-intf-named :{ "INTF_NAME" : "" }

  ietf-intf

  native-intf-named :{ "INTF_NAME" : "" }

  netconf-state

 

 

To get the configuration of all of the interfaces, use the "ietf-intf" filter.

 

$ ./ncc.py --host 10.10.6.2 --username sdn --password password --snippets ./snippets-xe --get-running --named-filter ietf-intf

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

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

    <interface>

      <name>GigabitEthernet0/0</name>

      <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"/>

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

    </interface>

 

 

Notice the "interfaces", "interface", "name", "type" and "enabled" tags from the YANG module.  There is another component "ipv4" (and "ipv6").  This is an example of augmentation, and will be covered later.

 

The contents of the filter "ietf-intf" are shown below.  It is very simple, just extracting the <interfaces> container from the configuration.

 

$ cat snippets-xe/filters/ietf-intf.tmpl

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

 

We can use another filter, to get the configuration of a specific interface.  The string {{INTF_NAME}} is a variable for a jinja template and can be provided as an option.  This allows you to get the configuration of a specific interface.


$ cat snippets-xe/filters/ietf-intf-named.tmpl

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

  <interface>

    <name>{{INTF_NAME}}</name>

  </interface>

</interfaces>

 

 

This allows you to get the configuration of a specific interface.  For example, to specify interface "GigabitEthernet1/0/1", you would provide a JSON dictionary of  {"variable":"value"}.  In this case '{"INTF_NAME" : "GigabitEthernet1/0/1"}.

 

$./ncc.py --host 10.10.6.2 --username sdn --password password --snippets ./snippets-xe --get-running --named-filter ietf-intf-named --params '{"INTF_NAME" : "GigabitEthernet1/0/1"}'

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

  <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>

 

 

Again, you can see that the "interfaces" module has been augmented with extra capabilities including <diffserv-target-entry.

Augmenting Modules

 

We saw earlier that the interfaces module had IP attributes that were not defined in the module.   A hint to finding them is contained in the namespace <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">.  The module "ietf-ip" will contain the augmentation of the interfaces module.

 

All of the Modules are published on github,  Cisco specific modules are located here https://github.com/YangModels/yang/tree/master/vendor/cisco/xe/1632  and standard models (e.g. IETF) are here https://github.com/YangModels/yang/tree/master/standard/ietf/RFC

 

The ietf.ip module can be downloaded here (in a later blog we will cover downloading the modules directly from the device).

https://github.com/YangModels/yang/blob/master/standard/ietf/RFC/ietf-ip%402014-06-16.yang

The file "ietf-ip.yang" file, contains the following lines:

<SNIPPED>

/*

  * Configuration data nodes

  */

augment "/if:interfaces/if:interface" {

   description

     "Parameters for configuring IP on interfaces.

      If an interface is not capable of running IP, the server

      must not allow the client to configure these parameters.";

   container ipv4 {

     presence

       "Enables IPv4 unless the 'enabled' leaf

        (which defaults to 'true') is set to 'false'";

     description

       "Parameters for the IPv4 address family.";

     leaf enabled {

       type boolean;

       default true;

       description

<SNIPPED>

 

 

interfaces/interface is being augmented with containers and leaves for ipv4.

 

We can visualise these extensions using the pyang tool, then look at the ietf-ip.html file in a browser. (I have saved the .yang files in a directory called YANG)

 

pyang -f jstree YANG/ietf-interfaces.yang YANG/ietf-ip.yang > /tmp/ietf-ip.html

ietf-int-ext.png

 

This shows the extra attributes added to the interface for ipv4 and ipv6 addresses.

Operational Data (oper-data)

 

NETCONF uses a Remote Procedure Call (RPC) approach.  We saw two examples of this earlier, <get-config> and <edit-config>. These were the two operations used to access and change the configuration.

 

NETCONF also supports a <get> RPC call.  This is used for "operational data" or statistics on the device.

In order to enable operational data, a chunk of NETCONF configuration needs to be applied.   We will use the "ncc.py" tool to do this.

 

To simplify the command line options, we set some environment variables to make using ncc.py a bit simpler.  These will set a default username, password and snippets directory.

export NCC_USERNAME=sdn

export NCC_PASSWORD=password

export NCC_SNIPPETS=snippets-xe

 

First, get the list of current configuration templates.   The "--list-templates" option does this.  Remember it is using the snippets directory specified in the environment variable above.


$ ./ncc.py --host 10.10.6.2  --list-templates

Edit-config templates:

  00-oper-data-enable-16.4

  00-oper-data-enable

  00-snmp-config

  01-oper-data-disable

  01-snmp-config-disable

  native-intf-vlan-change :{ "INTF_NAME" : "","VLAN" : "" }

 

 

We want to apply the 00-oper-data-enable template.  Take a look at the contents first.

 

$ cat snippets-xe/editconfigs/00-oper-data-enable.tmpl

<config>

      <netconf-yang xmlns="http://cisco.com/yang/cisco-self-mgmt">

        <cisco-odm xmlns="http://cisco.com/yang/cisco-odm">

         <polling-enable>true</polling-enable>

          <on-demand-default-time>30000</on-demand-default-time>

          <on-demand-enable>false</on-demand-enable>

          <actions>

<action-name>parse.showACL</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showArchive</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

            <action-name>parse.showEnvironment</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showFlowMonitor</action-name>

            <polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showInterfaces</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showIpRoute</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

            <action-name>parse.showMemoryStatistics</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showPlatformSoftware</action-name>

            <polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showProcessesCPU</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

          <actions>

<action-name>parse.showProcessesMemory</action-name>

<polling-interval>120000</polling-interval>

            <mode>poll</mode>

          </actions>

        </cisco-odm>

      </netconf-yang>

  </config>

 

 

This shows the polling that will be enabled on a 120second update cycle.  "parse.showInterfaces" enables interface statistics.

Now enable operational data using the following command.

 

$ ./ncc.py --host 10.10.6.2  --do-edits 00-oper-data-enable

 

 

We can now get oper-data. Remember from the earlier example of ietf-interfaces, there was a container called "interfaces-state". We will use an xpath instead of a filter, but it achieves a similar result. The first example will get all stats for all interfaces.

 

$ ./ncc.py --host 10.10.6.2  --get-oper -x /interfaces-state

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

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

    <interface>

      <name>GigabitEthernet0/0</name>

      <oper-status>down</oper-status>

      <phys-address>7c:95:f3:bd:2b:00</phys-address>

      <speed>1000000000</speed>

      <statistics>

        <in-octets>0</in-octets>

        <in-broadcast-pkts>0</in-broadcast-pkts>

        <in-multicast-pkts>0</in-multicast-pkts>

        <in-discards>0</in-discards>

        <in-errors>0</in-errors>

        <out-octets>956</out-octets>

        <out-discards>0</out-discards>

        <out-errors>0</out-errors>

        <in-pkts xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces-ext">0</in-pkts>

        <out-pkts xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces-ext">12</out-pkts>

      </statistics>

    </interface>

    <interface>

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

      <oper-status>up</oper-status>

      <phys-address>7c:95:f3:bd:2b:64</phys-address>

      <speed>1000000000</speed>

      <statistics>

        <in-octets>17658998</in-octets>

        <in-broadcast-pkts>15360</in-broadcast-pkts>

        <in-multicast-pkts>15359</in-multicast-pkts>

        <in-discards>0</in-discards>

        <in-errors>0</in-errors>

        <out-octets>64951892</out-octets>

        <out-discards>0</out-discards>

        <out-errors>0</out-errors>

        <in-pkts xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces-ext">185652</in-pkts>

        <out-pkts xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces-ext">302509</out-pkts>

      </statistics>

    </interface>

  <SNIPPED>

 

 

We could also just get the in-octets for GigabitEthernet1/0/1 if required.  Note the use of '' around the xpath to avoid UNIX shell issues.

 

 

$./ncc.py --host 10.10.6.2  --get-oper -x '/interfaces-state/interface[name="GigabitEthernet1/0/1"]/statistics/in-octets'

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

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

    <interface>

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

      <statistics>

        <in-octets>17723702</in-octets>

      </statistics>

    </interface>

  </interfaces-state>

</data>

 

 

You can specify multiple keys.  In this case we are interested in both "Gig1/0/1" and "G1/0/9". Note you need to specify the full name of the interface "GigabitEthernet1/0/1".

 

$./ncc.py --host 10.10.6.2  --get-oper -x '/interfaces-state/interface[name="GigabitEthernet1/0/1" or name="GigabitEthernet1/0/9"]/statistics/in-octets'

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

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

    <interface>

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

      <statistics>

        <in-octets>36977042</in-octets>

      </statistics>

    </interface>

    <interface>

    <name>GigabitEthernet1/0/9</name>

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

   </interface>

  </interfaces-state>

</data>

 

 

XPATH supports wildcards, so I could get all of the "in-octets" stats for all interfaces.  In the following example, the string "//" replaces "interface/statistics". Note that "name " is also returned because it is the key for the interface list.

 

$ ./ncc.py --host 10.10.6.2  --get-oper -x '/interfaces-state//in-octets'

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

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

    <interface>

      <name>GigabitEthernet0/0</name>

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

    </interface>

    <interface>

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

      <statistics>

     <in-octets>17741531</in-octets>

      </statistics>

    </interface>

    <interface>

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

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

    </interface>

    <interface>

    <name>GigabitEthernet1/0/11</name>

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

    </interface>

    <interface>

     <name>GigabitEthernet1/0/12</name>

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

    </interface>

    <interface>

     <name>GigabitEthernet1/0/13</name>

      <statistics>

        <in-octets>0</in-octets>

      </statistics>

    </interface>

    <interface>

  <SNIPPED>

 

 

Remember the XPATH statements can also be implemented as filters.

 

 

 

VLAN template

 

Remember the change VLAN example from the first blog.  I have turned that into a template that you can run from the ncc.py tool with parameters for interface and vlan.

 

$ cat snippets-xe/editconfigs/native-intf-vlan-change.tmpl

<config>

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

      <interface>

        <GigabitEthernet>

          <name>{{INTF_NAME}}</name>

          <switchport>

            <access>

              <vlan>

                <vlan>{{VLAN}}</vlan>

              </vlan>

            </access>

          </switchport>

        </GigabitEthernet>

      </interface>

    </native>

 

 

The template requires two variables "INTF_NAME" and "VLAN". Remember that the interface name will be just the number "1/0/9" as the native model has an implicit "GigabitEthernet" in the outside container.

 

If you list the templates , you will see an empty JSON dictionary you can provide with parameters for the call.

$ ./ncc.py --host 10.10.6.2 --list-templates

Edit-config templates:

  00-oper-data-enable-16.4

  00-oper-data-enable

  00-snmp-config

  01-oper-data-disable

  01-snmp-config-disable

  native-intf-vlan-change :{ "INTF_NAME" : "","VLAN" : "" }

 

 

Now can run the template

 

$ ./ncc.py --host 10.10.6.2 --do-edits native-intf-vlan-change --params '{"INTF_NAME":"1/0/9","VLAN":"20"}'

 

 

A get-running will show the vlan has been changed to vlan 20.

 

$ ./ncc.py --host 10.10.6.2 --get-running –x '/native//GigabitEthernet[name="1/0/9"]//vlan/vlan'

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

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

    <interface>

      <GigabitEthernet>

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

        <switchport>

          <access>

            <vlan>

              <vlan>20</vlan>

            </vlan>

          </access>

        </switchport>

      </GigabitEthernet>

    </interface>

  </native>

</data>

 

 

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 advanced snippet templates, transactions, downloading modules, SNMP MIBs and RESTCONF.

 

Thanks for reading,

 

@adamradford123

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