The Problem with SDN Integration Testing
Traditional OpenDaylight integration tests require starting a full Karaf container and connecting to real or separately-launched virtual devices. This makes tests slow (often minutes to hours in CI), hard to debug, and brittle across environments.
lighty.io solves this by providing all three layers of a test network in a single JVM:
- Layer 1: RESTCONF client (lighty.io client library)
- Layer 2: lighty.io SDN controller (RESTCONF northbound + NETCONF southbound)
- Layer 3: NETCONF device simulators (Toaster device + Topology device)
Test Architecture
The test setup spins up a complete mini SDN network on localhost, using different ports for each component. YANG models are shared across all layers to guarantee type safety end-to-end.
// All three layers start in a single @BeforeClass
@BeforeClass
public static void beforeClass() throws Exception {
totalDuration = System.nanoTime();
// Layer 3: start NETCONF device simulators
toasterDevice = NetconfDeviceSimulator.start(TOASTER_PORT, toasterYangModels);
topologyDevice = NetconfDeviceSimulator.start(TOPOLOGY_PORT, topologyYangModels);
// Layer 2: start lighty SDN controller
Main.start();
// Layer 1: connect REST client to controller
device = new BindingAwareRestConnectorBuilder()
.from(new InetSocketAddress("localhost", 8888), keyStore)
.build();
// Connect both simulators to the controller via RESTCONF
device.connectNetconfDevice("toaster-device", "localhost", TOASTER_PORT);
device.connectNetconfDevice("topology-device", "localhost", TOPOLOGY_PORT);
}
Test Cases & Timing
| Test | Duration | Description |
|---|---|---|
| Controller startup | ~2s | Start controller + both device simulators |
| testRestClientCreateSubscriptionAll | ~0.3s | Subscribe to RESTCONF notifications on Topology device and trigger via RPC |
| testToasterReadDeleteDatastoreOperations | ~0.1s | Read and delete datastore operations on Toaster device |
| testTopologyDeviceRPCInputOutput | ~0.6s | Call RPCs with input/output on Topology device |
| Stop all & wait | ~0.08s | Graceful shutdown of all three layers |
Sample Test Method
@Test
public void testToasterReadDeleteDatastoreOperations() throws Exception {
// Read toaster operational data via mount point
final BindingAwareMountRestDevice mountPoint =
device.getMountPoint("toaster-device");
final Optional<Toaster> toasterData = mountPoint
.get(InstanceIdentifier.create(Toaster.class),
QueryParametersBuilder.content("nonconfig"))
.get().getData();
Assert.assertTrue(toasterData.isPresent());
Assert.assertNotNull(toasterData.get().getDarknessFactor());
}
Sanity Test Example
@Test
public void sanityTest() throws Exception {
// As this test starts full instance of ODL controller and RESTCONF
// http server on port 8888, this port must be free prior to the test.
ContentResponse get = httpClient
.GET("http://localhost:8888/restconf/data/network-topology:network-topology");
Assert.assertEquals(get.getStatus(), 200);
}
@AfterTest
public void destroy() throws Exception {
Main.shutdown();
httpClient.stop();
}
Hardware Configuration
All benchmarks run on: Intel i7-4810MQ CPU · 16 GB DDR3 RAM · JDK 8 · Ubuntu 18.04 LTS
What Makes This Possible
- No Karaf boot overhead — lighty.io starts in ~2s vs minutes for ODL
- NETCONF device simulators run in the same JVM — no network round-trips to separate processes
- RESTCONF client library included — no external tool needed to drive the controller
- Shared YANG models across all layers — type-safe from device to REST client
- Works in any CI/CD pipeline with no special infrastructure requirements