WELCOME TO THE Donald Hunter Cisco
SCA-API OpenDaylight Project
SCA-API OpenDaylight Project Implement the MEF SCA / NRP API Built as an OpenDaylight plugin Northbound REST SCA-API Southbound NETCONF to Cisco ASR-9001 devices Implement the SCA-API using Jersey JAX-RS Use MD-SAL bindings for model to model transformation
SCA-API OpenDaylight Project Leverage OpenDaylight services and architecture YANG driven tooling Auto-generates NBI, Java bindings, etc. Model Driven Service Abstraction Layer – MD-SAL Topology Multiple southbound protocol drivers NETCONF, OpenFlow, etc. Model-Driven SAL (MD-SAL) is a set of infrastructure services aimed at providing common and generic support to application and plugin developers. MD-SAL currently provides infrastructure services for: Data Store RPC / Service routing Notification subscription and publish services This common model-driven infrastructure allows developers of applications and plugins to develop against one set of APIs that are derived from a single model: Java generated APIs, DOM APIs, and REST APIs.
SCA-API Project Architecture <<REST>> SCA-API OpenDaylight sca-api-web mef-sca-model MD-SAL sca-api-provider XRmodels NETCONF southbound driver
Implementation Strategy Implement JAX-RS API as a thin layer on top of MD-SAL YANG schema generated from Swagger specification Demonstrates interoperability between JAX-RS and RESTCONF Focuses development effort towards MD-SAL APIs Use NETCONF config model, not RPCs Has equivalent semantics to JAX-RS API Business logic driven by MD-SAL change events
Developer Skills Java development with OSGi JAX-RS development – we are using Jersey MD-SAL binding APIs YANG schema language – https://tools.ietf.org/html/rfc6020
Developer Tools Required Useful Java – OpenJDK 7 or 8, Oracle JDK 7_45 or later Maven – with OpenDaylight specific settings.xml Useful Eclipse, IntelliJ, NetBeans or equivalent SDK pyang Postman
Tutorial
Introduction to OpenDaylight We are using OpenDaylight Lithium which runs in the Karaf OSGi container The git branch is stable/lithium The maven version is 0.3.3-SNAPSHOT Documentation available at: https://www.opendaylight.org/downloads
Running OpenDaylight $ tar xf distribution-karaf-0.3.3-SNAPSHOT.tar.gz $ cd distribution-karaf-0.3.3-SNAPSHOT/ $ ./bin/karaf ________ ________ .__ .__ .__ __ \_____ \ ______ ____ ____ \______ \ _____ ___.__.| | |__| ____ | |___/ |_ / | \\____ \_/ __ \ / \ | | \\__ \< | || | | |/ ___\| | \ __\ / | \ |_> > ___/| | \| ` \/ __ \\___ || |_| / /_/ > Y \ | \_______ / __/ \___ >___| /_______ (____ / ____||____/__\___ /|___| /__| \/|__| \/ \/ \/ \/\/ /_____/ \/ Hit '<tab>' for a list of available commands and '[cmd] --help' for help on a specific command. Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown OpenDaylight. opendaylight-user@root>
Managing Features When karaf starts, there are no OpenDaylight features installed Use feature:install to add features – usually odl-*-all opendaylight-user@root>feature:list | grep dlux odl-dlux-all | 0.2.3-SNAPSHOT | odl-dlux-0.2.3-SNAPSHOT | Opendaylight dlux all features odl-dlux-core | 0.2.3-SNAPSHOT | odl-dlux-0.2.3-SNAPSHOT | Opendaylight dlux minimal feature odl-dlux-node | 0.2.3-SNAPSHOT | odl-dlux-0.2.3-SNAPSHOT | Enable nodes in Opendaylight dlux odl-dlux-yangui | 0.2.3-SNAPSHOT | odl-dlux-0.2.3-SNAPSHOT | Enable Yang UI in Opendaylight dlux odl-dlux-yangvisualizer | 0.2.3-SNAPSHOT | odl-dlux-0.2.3-SNAPSHOT | Enable Yang visualizer in dlux odl-snbi-dlux | 1.1.3-SNAPSHOT | odl-snbi-1.1.3-SNAPSHOT | OpenDaylight :: SNBI :: Dlux opendaylight-user@root>feature:install odl-dlux-all opendaylight-user@root>
http://localhost:8181/index.html – admin/admin
Diagnostics OpenDaylight console has useful diagnostic commands bundle:list log:display log:tail web:list http:list opendaylight-user@root>bundle:list | grep sca 236 | Active | 80 | 1.0.0.SNAPSHOT | org.mef.odl.sca-api-model 237 | Active | 80 | 1.0.0.SNAPSHOT | org.mef.odl.sca-api-xrmodels 238 | Active | 80 | 1.0.0.SNAPSHOT | sca-api-provider 243 | Active | 80 | 1.0.0.SNAPSHOT | sca-api-web opendaylight-user@root>log:tail 2015-11-13 17:15:53,078 | WARN | sing-executor-11 | NetconfTopologyChangeListener | 238 - org.mef.odl.sca-api-provider - 1.0.0.SNAPSHOT | Failed to read interfaces for ... 2015-11-13 17:15:53,124 | WARN | sing-executor-11 | NetconfTopologyChangeListener | 238 - org.mef.odl.sca-api-provider - 1.0.0.SNAPSHOT | Failed to read interfaces for ... 2015-11-13 17:15:53,132 | INFO | sing-executor-11 | NetconfDevice | 169 - org.opendaylight.controller.sal-netconf-connector - 1.2.3.SNAPSHOT | RemoteDevice{controller-config}: Netconf connector initialized successfully opendaylight-user@root>web:list ID | State | Web-State | Level | Web-ContextPath | Name ------------------------------------------------------------------------------------------------------------- 218 | Active | Deployed | 80 | /oauth2 | aaa-authn-sts (0.2.3.SNAPSHOT) 222 | Active | Deployed | 80 | /auth | aaa-idmlight (0.2.3.SNAPSHOT) 231 | Active | Deployed | 80 | /oauth2/federation | aaa-authn-federation (0.2.3.SNAPSHOT) 240 | Active | Deployed | 80 | /restconf | MD SAL Restconf Connector (1.2.3.SNAPSHOT) 243 | Active | Deployed | 80 | /sca-api | sca-api-web (1.0.0.SNAPSHOT) 272 | Active | Deployed | 80 | /apidoc | MD SAL Rest Api Doc Generator (1.2.3.SNAPSHOT)
Anatomy of an OpenDaylight Project An OpenDaylight plugin is a maven project sca-api – the parent project model – data / api model for the project – in YANG provider – module that implements YANG rpc calls client – module that consumes the provided model web – module that extends the OpenDaylight web interface features – creates a karaf feature to package everything distribution-karaf – creates a custom OpenDaylight karaf distribution
SCA-API OpenDaylight Project
Project Tour - models % ls model/src/main/yang mef-global.yang mef-interfaces.yang mef-sca-api.yang <- Generated from jtpugac/MEF-SCA/1.0 mef-services.yang mef-topology.yang mef-types.yang % ls xrmodels/src/main/yang Cisco-IOS-XR-ifmgr-cfg@2015-01-07.yang Cisco-IOS-XR-ip-static-cfg@2015-01-07.yang Cisco-IOS-XR-l2-eth-infra-cfg@2015-01-07.yang Cisco-IOS-XR-l2-eth-infra-datatypes@2015-01-07.yang Cisco-IOS-XR-l2vpn-cfg@2015-01-07.yang
Project Tour - provider % ls provider/src/main/java/org/mef/sca/provider NetconfNodeChangeListener.java NetconfTopologyChangeListener.java ScaApiProvider.java UniChangeListener.java % ls provider/src/main/yang sca-api-provider-impl.yang % ls provider/src/main/resources/configuration/initial 05-provider-config.xml
Project Tour - web % ls web/src/main/java/org/mef/sca/jaxrs JaxRSApplication.java ODL.java ScaApi.java % ls web/src/main/resources/WEB-INF web.xml % ls web/src/main/yang/ sca-api-web.yang % ls web/src/main/resources/configuration/initial 10-sca-api-web-config.xml
Project Tour – features / distribution-karaf % ls features/src/main/resources features.xml % ls distribution-karaf pom.xml
Anatomy of an OpenDaylight Jar META-INF/services org.opendaylight.controller.config.spi.ModuleFactory # org.opendaylight.controller.config.yang.config.sca_api_provider.impl.ScaApiProviderModuleFactory org.opendaylight.yangtools.yang.binding.YangModuleBindingProvider # org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config # .sca.api.provider.impl.rev151028.$YangModelBindingProvider Tells OpenDaylight which Factory classes to use A YANG schema defines module wiring requirements Factory class is auto-generated from YANG schema An XML config file provides module wiring instance data
META-INF/yang/sca-api-provider-impl.yang module sca-api-provider-impl { prefix "sca-api-provider-impl”; ... augment "/config:modules/config:module/config:configuration" { case sca-api-provider-impl { when "/config:modules/config:module/config:type = 'sca-api-provider-impl'”; container binding-aware-broker { uses config:service-ref { refine type { mandatory true; config:required-identity mdsal:binding-broker-osgi-registry; } Declare which services should be wired into module. This drives code-generation
etc/opendaylight/karaf/05-provider-config.xml <snapshot> <configuration> <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> <module> <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:sca-api-provider:impl"> prefix:sca-api-provider-impl</type> <name>sca-api-provider-impl</name> <binding-aware-broker> <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding"> binding:binding-broker-osgi-registry</type> <name>binding-osgi-broker</name> </binding-aware-broker> </module> </modules> </data> </configuration> </snapshot> Provide the binding data for runtime initialization. This config file is read at startup.
The Module Provider public class ScaApiProviderModule extends org.opendaylight.controller.config.yang.config.sca_api_provider.impl.AbstractScaApiProviderModule { ... public java.lang.AutoCloseable createInstance() { DataBroker dataBroker = getDataBrokerDependency(); # auto-generated accessor final ScaApiProvider provider = new ScaApiProvider(dataBroker); # the provider implementation BindingAwareBroker bindingBroker = getBindingAwareBrokerDependency(); # auto-generated accessor bindingBroker.registerProvider(provider); final class CloseResources implements AutoCloseable { public void close() throws Exception { provider.close(); } return new CloseResources(); The ProviderModule extends an auto-generated class that implements the runtime binding.
Provider Implementation public class ScaApiProvider implements BindingAwareProvider, AutoCloseable { private DataBroker dataBroker; private MountPointService mountService; public ScaApiProvider(DataBroker broker) { dataBroker = broker; } public void onSessionInitiated(ProviderContext session) { # called after provider registration mountService = session.getSALService(MountPointService.class); # used to access NETCONF devices
Adding Behaviour to Provider – 1 public class ScaApiProvider implements BindingAwareProvider, AutoCloseable { private ListenerRegistration<DataChangeListener> topoRegistration; public void close() throws Exception { topoRegistration.close(); } public void onSessionInitiated(ProviderContext session) { InstanceIdentifier<Topology> idTopo = InstanceIdentifier.builder(NetworkTopology.class) .child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))) .build(); topoRegistration = dataBroker.registerDataChangeListener( LogicalDatastoreType.OPERATIONAL, idTopo.child(Node.class), new NetconfTopologyChangeListener(dataBroker, mountService), DataChangeScope.SUBTREE); Instance identifiers are used to uniquely identify all MD-SAL nodes Register for add / remove NETCONF devices.
Adding Behaviour to Provider – 2 public class NetconfTopologyChangeListener implements DataChangeListener, MountPointListener { public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) { for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) { DataObject dataObject = entry.getValue(); if (dataObject instanceof Node) { NodeId nodeId = entry.getKey().firstKeyOf(Node.class, NodeKey.class).getNodeId(); final String routerName = nodeId.getValue(); log.info("ADDED {} path {}", routerName, entry.getKey()); mountService.registerListener(entry.getKey(), this); } Map<InstanceIdentifier<?>, DataObject> originalData = change.getOriginalData(); for (InstanceIdentifier<?> path : change.getRemovedPaths()) { DataObject dataObject = originalData.get(path); log.info("REMOVED path {}", path); Handling NETCONF topology change events.
Adding Behaviour to Provider – 3 public class NetconfTopologyChangeListener implements DataChangeListener, MountPointListener { public void onMountPointCreated(InstanceIdentifier<?> path) { fetchNodeInterfaces(path); // NETCONF Device has been added } public void onMountPointRemoved(InstanceIdentifier<?> path) { // NETCONF Device has been removed protected void fetchNodeInterfaces(InstanceIdentifier<?> nodeId) { final Optional<MountPoint> netconfNodeOptional = mountService.getMountPoint(nodeId); if (netconfNodeOptional.isPresent()) { MountPoint netconfNode = netconfNodeOptional.get(); DataBroker netconfNodeDataBroker = netconfNode.getService(DataBroker.class).get(); InstanceIdentifier<InterfaceConfigurations> icId = InstanceIdentifier.builder(InterfaceConfigurations.class).build(); ReadOnlyTransaction t = netconfNodeDataBroker.newReadOnlyTransaction(); CheckedFuture<Optional<InterfaceConfigurations>, ReadFailedException> future = t.read(LogicalDatastoreType.OPERATIONAL, icId); ... Get a NETCONF device’s mount point. Query the device’s operational data model.
The Project Feature Feature dependencies. Required bundles. <features name="task-${project.version}” xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> <feature name='odl-sca-api-provider' version='${project.version}'> <feature version='${yangtools.version}'>odl-yangtools-common</feature> <feature version='${yangtools.version}'>odl-yangtools-binding</feature> <feature version='${mdsal.version}'>odl-mdsal-broker</feature> <bundle>mvn:org.mef.odl/${artifactName}-model/${project.version}</bundle> <bundle>mvn:org.mef.odl/${artifactName}-xrmodels/${project.version}</bundle> <bundle>mvn:org.mef.odl/${artifactName}-provider/${project.version}</bundle> <configfile finalname="${config.configfile.directory}/05-task-provider-config.xml"> mvn:org.mef.odl/${artifactName}-provider/${project.version}/xml/config</configfile> </feature> ... </features> Feature dependencies. Required bundles. Bundle wiring configuration file.
Packaging a Karaf Distribution <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <artifactId>distribution-karaf</artifactId> ... <build> <plugins> <plugin> <groupId>org.apache.karaf.tooling</groupId> <artifactId>karaf-maven-plugin</artifactId> <configuration> <bootFeatures> <feature>standard</feature> <feature>odl-sca-api-web</feature> <feature>odl-netconf-connector-all</feature> <feature>odl-dlux-all</feature> </bootFeatures> </configuration> </plugins> </build> </project> Choose the boot features you want in your karaf distribution..
Getting the Code The SCA-API project is available in the MEF-GIT organization on Github: https://github.com/MEF-GIT/SCA-API git clone https://github.com/MEF-GIT/SCA-API.git
Useful Links Lithium download and developers guide https://www.opendaylight.org/downloads SDNHub OpenDaylight Tutorial http://sdnhub.org/tutorials/opendaylight/ SDNHub NETCONF Connector Tutorial http://sdnhub.org/tutorials/opendaylight-tutorial/experimenting-with-netconf-connector-in-opendaylight/ OpenDaylight setup scripts https://github.com/CiscoDevNet/opendaylight-sample-apps/tree/master/odl-setup OpenDaylight Wiki entries https://communities.cisco.com/people/giheron?view=content Useful Postman collections https://github.com/CiscoDevNet/opendaylight-sample-apps/tree/master/postman-collections
Samples
L2VPN Cross Connect NETCONF Payload <xconnect-group xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-l2vpn-cfg"> <name>GEN15</name> <p2p-xconnects> <p2p-xconnect> <name>EPL1</name> <attachment-circuits> <attachment-circuit> <name>TenGigE0/0/2/0.101</name> <enable></enable> </attachment-circuit> <name>GigabitEthernet0/0/1/0</name> </attachment-circuits> </p2p-xconnect> </p2p-xconnects> </xconnect-group>
SCA Flow Domain Fragment Payload { "uniqueId": "string", "state": "active", "evcServiceType": "Point_To_Point", "evcCfgIdentifier": "string", "evcStatusMaxNumUni": 0, "evcCfgMtuSize": 0, "evcCfgCeVlanIdPreservation": "Enabled", "evcCfgCeVlanCosPreservation": "Enabled", "SCA_ETH_Flow_Points": [ { "evcPerUniCfgIdentifier": "string", "scaEthFppUniN": { "transportPort": { "href": "string", "CLLI": "string", "Vendor": "string", "Hostname": "string", "Shelf": "string", "Slot": "string", "Subslot": "string", "Port": "string", "Rack": "string", . . .