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:

After moving from upstream OpenDaylight to lighty.io, our projects are compiled and tested in seconds or minutes, rather than hours.

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

~12sTotal test run
~2sController startup
~0.08sFull shutdown
TestDurationDescription
Controller startup~2sStart controller + both device simulators
testRestClientCreateSubscriptionAll~0.3sSubscribe to RESTCONF notifications on Topology device and trigger via RPC
testToasterReadDeleteDatastoreOperations~0.1sRead and delete datastore operations on Toaster device
testTopologyDeviceRPCInputOutput~0.6sCall RPCs with input/output on Topology device
Stop all & wait~0.08sGraceful 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