On the southbound, OpenDaylight has support for both NETCONF and OpenFlow for managing underlying devices. This allows your applications to manage both traditional devices with in-built control planes and the OpenFlow-enabled devices with decoupled control planes. End-to-end control and orchestration is, thus, easily achieved using a single platform.
1. Setup
In this post we get an understanding of the NETCONF support through a simple experiment with netopeer‘s NETCONF server packaged as a Docker image. Here are steps to get a hands-on experience into using NETCONF:
Step 1: Install Docker and netopeer image.
$ wget -qO- https://get.docker.com/gpg | sudo apt-key add - $ wget -qO- https://get.docker.com/ | sudo sh $ sudo docker pull sdnhub/netopeer
Step 2: Spin up simulated router instances. We now spin up two simulated router instances (
router1and
router2) that can be configured over NETCONF. (Assuming you have the SDNHub_OpenDaylight_Tutorial folder cloned)
$ git clone https://github.com/sdnhub/SDNHub_Opendaylight_Tutorial/ $ cd SDNHub_Opendaylight_Tutorial/netconf-exercise $ ./spawn_router.sh router1 ... Spawned container with IP 172.17.0.63 $ ./spawn_router.sh router2 ... Spawned container with IP 172.17.0.64
These two simulated routers use the following custom YANG model to model some basic configurations of typical routers.
module router { yang-version 1; namespace "urn:sdnhub:odl:tutorial:router"; prefix router; description "Router configuration"; revision "2015-07-28" { description "Initial version."; } list interfaces { key id; leaf id { type string; } leaf ip-address { type string; } } container router { list ospf { key process-id; leaf process-id { type uint32; } list networks { key subnet-ip; |
leaf subnet-ip { type string; } leaf area-id { type uint32; } } } list bgp { key as-number; leaf as-number { type uint32; } leaf router-id { type string; } list neighbors { key as-number; leaf as-number { type uint32; } leaf peer-ip { type string; } } } } } |
Step 3: Start OpenDaylight and register the two NETCONF devices. In this example, we use the pre-compiled OpenDaylight in the tutorial VM. Ensure that you install the
odl-netconf-connector-allfeature
$ cd ~/SDNHub_Opendaylight_Tutorial $ mvn clean install -nsu $ cd distribution/opendaylight-karaf/target/assembly $ ./bin/karaf opendaylight-user@root> feature:install odl-netconf-connector-all
After waiting about 30 secs, register the two NETCONF-enabled routers with OpenDaylight using shell scripts provided. Ensure that the IP address below is updated to what was printed by the spawn_router script above.
$ ./register_netconf_device.sh router1 172.17.0.63; sleep 5 $ ./register_netconf_device.sh router2 172.17.0.64; sleep 5
2. Using RESTconf to manage the devices
Now that you have registered the two NETCONF speakers, you will be able to get and edit configurations, and perform RPC calls from the controller
- You can see all the YANG models support by that device at this location.
- For instance, for each router we spawned, you see the following capability in that list:
<available-capability> (urn:sdnhub:odl:tutorial:router?revision=2015-07-28)router </available-capability>
- This works only if your device has support for
ietf-netconf-monitoring
, wherein devices export their capabilities or modules to the NETCONF client. - If you are attempting to manage a device that does not support
ietf-netconf-monitoring
, there are other ways to add the YANG models directly at the controller.
We can start posting configurations from the OpenDaylight controller to the NETCONF servers in the routers using the router YANG model shown above. For instance, we can inject configurations to
router1using RESTCONF as follows:
$ curl -u admin:admin -v -X PUT -H "Content-Type: application/xml" -d ' <interfaces xmlns="urn:sdnhub:odl:tutorial:router"> <id>eth0</id> <ip-address>10.10.1.1/24</ip-address> </interfaces> ' http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/router1/yang-ext:mount/router:interfaces/eth0
Visit this page after making the above cUrl call to verify that the configuration was added.
Alternatively, you can use netopeer-cli (which is a NETCONF client) to verify the same data as follows:
$ sudo docker attach router1 # netopeer-cli netconf> connect localhost (Passwd: root) netconf> get-config running Result: <interfaces xmlns="urn:sdnhub:odl:tutorial:router"> <id>eth0</id> <ip-address>10.10.1.1/24</ip-address> </interfaces> netconf>
As you can see, our RESTconf operation pushed configuration from the OpenDaylight controller to the data store of the device’s NETCONF server. The traditional control plane will, then, pass those configurations to the routing protocols within.
You can further experiment with this environment and push other configurations (like BGP and OSPF) for these two routers. Here is an example of how you can configure OSPF and BGP parameters (assuming all the interface IP are assigned prior to this):
$ curl -u admin:admin -v -X PUT -H "Content-Type: application/xml" -d ' <router xmlns="urn:sdnhub:odl:tutorial:router"> <ospf> <process-id>1</process-id> <networks> <subnet-ip>100.100.100.0/24</subnet-ip> <area-id>10</area-id> </networks> </ospf> <bgp> <as-number>1000</as-number> <router-id>10.10.1.1</router-id> <neighbors> <as-number>2000</as-number> <peer-ip>10.10.1.2</peer-ip> </neighbors> </bgp> </router> ' http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/router1/yang-ext:mount/router:router
Here is a postman collections file with a few REST calls in case you want to add or get configurations.
3. Developing a NETCONF application in Java
Within the tutorial project, we have a Java application that uses the OpenDaylight NETCONF connector to program configurations to the underlying device. This is to illustrate the type of automation possible within the JVM. This is also a way to logically centralize state pertaining all types of devices, both OpenFlow-based devices and legacy ones, allowing for backward compatibility.
To enable this application, you will have to install the appropriate feature in the Karaf console, prior to registering the NETCONF devices using the “register_netconf_device.sh” script.
opendaylight-user@root> feature:install sdnhub-tutorial-netconf-exercise opendaylight-user@root> log:set DEBUG org.sdnhub
Now run “register_netconf_device.sh” in another terminal for router1. That in turn will print the following output in the console, confirming that configuration has been written to data store.
opendaylight-user@root> log:tail 2015-09-05 13:38:12,560 | INFO | sing-executor-15 | NetconfDevice | 244 - org.opendaylight.controller.sal-netconf-connector - 1.2.0.Lithium | RemoteDevice{router1}: Netconf connector initialized successfully 2015-09-05 13:38:14,114 | DEBUG | pool-52-thread-1 | MyRouterOrchestrator | 274 - org.sdnhub.odl.tutorial.netconf-exercise.impl - 1.0.0.SNAPSHOT | Configuring interface eth0 for router1 2015-09-05 13:38:14,417 | DEBUG | pool-52-thread-1 | GenericTransactionUtils | 274 - org.sdnhub.odl.tutorial.netconf-exercise.impl - 1.0.0.SNAPSHOT | Transaction success for add of object Interfaces [_id=eth0, _ipAddress=10.10.1.1/24, _key=InterfacesKey [_id=eth0], augmentation=[]] 2015-09-05 13:38:14,417 | DEBUG | pool-52-thread-1 | MyRouterOrchestrator | 274 - org.sdnhub.odl.tutorial.netconf-exercise.impl - 1.0.0.SNAPSHOT | Configuring interface eth1 for router1 2015-09-05 13:38:14,695 | DEBUG | pool-52-thread-1 | GenericTransactionUtils | 274 - org.sdnhub.odl.tutorial.netconf-exercise.impl - 1.0.0.SNAPSHOT | Transaction success for add of object Interfaces [_id=eth1, _ipAddress=100.100.100.1/24, _key=InterfacesKey [_id=eth1], augmentation=[]] ...
The implementation for the netconf-exercise (i.e.,
netconf-exercise-impl) has a helper class called
MyRouterOrchestratorthat inserts interface configurations when it discovers a device called
router1or
router2. Visit this page for router1 after registering the device to verify that the appropriate interface configurations were added.
The basic constructs necessary for working with a NETCONF device are as follows:
public class MyRouterOrchestrator implements BindingAwareProvider, AutoCloseable, DataChangeListener { private MountPointService mountService; .... @Override public void onSessionInitiated(ProviderContext session) { // Get the mount service provider this.mountService = session.getSALService(MountPointService.class); } //When new devices are discovered through a data change notification @Override public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) { .... //Step 1: Get access to mount point specific to this netconf node. InstanceIdentifier<Node> netconfNodeIID = InstanceIdentifier.builder(NetworkTopology.class) .child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))) .child(Node.class, new NodeKey(new NodeId("router1"))) .build(); final Optional<MountPoint> netconfNodeOptional = mountService.getMountPoint(netconfNodeIID); //Step 2: Write config data to the mount point if (netconfNodeOptional.isPresent()) { //Step 2.1: access data broker within this mount point MountPoint netconfNode = netconfNodeOptional.get(); DataBroker netconfNodeDataBroker = netconfNode.getService(DataBroker.class).get(); //Step 2.2: construct data and the instance identifier for the config data InterfacesBuilder builder = new InterfacesBuilder() .setId(interfaceName) .setKey(new InterfacesKey("eth0")) .setIpAddress("10.10.10.1/24"); InstanceIdentifier<Interfaces> interfacesIID = InstanceIdentifier .builder(Interfaces.class, builder.getKey()).build(); //Step 2.3: write to the config data store using SDN Hub special transaction utils. //The netconf connector sends the new config data to the device as an "edit-config" operation. GenericTransactionUtils.writeData(netconfNodeDataBroker, LogicalDatastoreType.CONFIGURATION, interfacesIID, builder.build(), true); } } }
The above skeleton code skips several steps to just highlight the basic constructs for mounting device configuration and editing it. Any changes made to the MD-SAL data store is passed along to the device. Similarly, when the controller boots up, any existing configuration is copied over from the device.
4. Summary
- This post illustrates how easy it is to configure legacy routers over NETCONF, as long as you have the YANG model for that device. This can be extremely powerful as a global orchestrator that nicely integrates OpenFlow and traditional control planes.
- Having said that, to manage different types of devices, one needs to build YANG-specific plugins or adapters to appropriately convert from high-level intent to device-specific configurations.
- This device-specific adaptation can be avoided if device configurations share a common model, like OpenConfig, for the common denominator of configurations for a protocol.