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.
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 .
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>
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.
YANG is much simpler and easier to understand. Looking at the interfaces module, there are a couple of core concepts.
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.
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
This shows the extra attributes added to the interface for ipv4 and ipv6 addresses.
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.
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>
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,
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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: