Migration of an ODL application to lighty.io is simple and straightforward. In this post, we are going to show you how to do this with help of TransportPCE ODL project acting as the example. Users can find many examples shipped along with light.io package to help them jumpstart projects, but this post will detail migration process in particular.

The major part of the migration is really removing hard dependencies on OSGI/Karaf. This exercise is good for the ODL project itself, as these dependencies should not exist in first place. We recommend sending the resulting patch to upstream track as a step towards cleaner code.

Since there are no OSGI dependencies in TransportPCE project, we will focus on the rest of the work that needs to be done. To begin, you need to find out what other ODL modules (e.g. controller, NETCONF, RESTCONF) the given project uses (depends on). TransportPCE’s dependencies are already migrated to lighty.io and therefore it is as easy as an initialization of an object and calling a start function.

Initializing object and calling a start function:

        //1. initialize and start OpenDaylight controller (MD-SAL, Controller, YangTools, Akka)
        final LightyControllerBuilder lightyControllerBuilder = new LightyControllerBuilder();
        final LightyController lightyController = lightyControllerBuilder.from(controllerConfiguration).build();
        lightyController.start().get();

Second step is to build the lighty.io TransportPCE object. To do this you have to go to the original project and find all the blueprint files. These will specify the dependency references that are needed by your newly created object constructor and beans, which are the objects that you will create using those references. These beans also have names of functions that you need for initializing and destroying of the object blueprint example:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
  odl:use-default-for-reference-types="true">

  <reference id="dataBroker"
    interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
    odl:type="default" />
  <reference id="mountPointService"
    interface="org.opendaylight.controller.md.sal.binding.api.MountPointService" />
  <reference id="rpcProviderRegistry"
    interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry" />
  <bean id="provider"
    class="org.opendaylight.transportpce.renderer.RendererProvider"
    init-method="init" destroy-method="close">
    <argument ref="dataBroker" />
    <argument ref="mountPointService" />
    <argument ref="rpcProviderRegistry" />
  </bean>
</blueprint>

Next step is to initialize all the objects that TransportPCE needs to function. lighty.io has an abstract helper class called “AbstractLightyModule” which you should extend. With this, you will be overriding an “initProcedure” function and whenever you call “start” on your lighty.io object, this function will be triggered. In this function, all you need to do is to create all the beans from blueprints and call their initializing function.

Start method from abstract class:

public synchronized ListenableFuture<Boolean> start() {
        if(this.running) {
            LOG.warn("LightyModule {} is already started.", this.getClass().getSimpleName());
            return Futures.immediateFuture(Boolean.valueOf(true));
        } else {
            if(this.executorService == null) {
                LOG.debug("Creating default single thread ExecutorService for LightyModule {}.", this.getClass().getSimpleName());
                this.executorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
            }

            LOG.info("Submitted start of LightyModule {}.", this.getClass().getSimpleName());
            return this.executorService.submit(() -> {
                synchronized(this) {
                    LOG.debug("Starting initialization of LightyModule {}", this.getClass().getSimpleName());
                    boolean initResult = this.initProcedure();
                    this.running = true;
                    LOG.info("LightyModule {} started.", this.getClass().getSimpleName());
                    return Boolean.valueOf(initResult);
                }
            });
        }
    }

Initializing and calling start on lightyTransportPce object:

        //start TransportPce application
        final LightyTransportPce lightyTransportPce;
        final TransportPceBuilder lightyTransportPceBuilder = new TransportPceBuilder();
        lightyTransportPce = lightyTransportPceBuilder.from(lightyController.getServices()).build();
        lightyTransportPce.start().get();

overriding a initProcedure method:
    @Override
    protected boolean initProcedure() {
        final long startTime = System.nanoTime();
        LOG.info("Starting TransportPceProvider");
        transportpceProvider =
                new TransportpceProvider(dataBroker, mountPointService);
        transportpceProvider.init();

        LOG.info("Starting RendererProvider");
        rendererProvider = new RendererProvider(dataBroker, mountPointService,
                rpcProviderRegistry);
        rendererProvider.init();

        LOG.info("Starting OlmProvider");
        olmProvider = new OlmProvider(dataBroker, mountPointService, rpcProviderRegistry);
        olmProvider.init();

        LOG.info("Starting StubrendererProvider");
        stubrendererProvider = new StubrendererProvider(rpcProviderRegistry,
                notificationService,notificationPublishService);
        stubrendererProvider.init();

        LOG.info("Starting StubpceProvider");
        stubpceProvider = new StubpceProvider(rpcProviderRegistry, notificationService,
                notificationPublishService);
        stubpceProvider.init();

        LOG.info("Starting ServiceHandlerProvider");
        servicehandlerProvider = new ServicehandlerProvider(dataBroker, rpcProviderRegistry,
                notificationService, notificationPublishService);
        servicehandlerProvider.init();

        final float delay = (System.nanoTime() - startTime) / 1_000_000f;
        LOG.info("Lighty transportpce started in {}ms", delay);
        return true;
    }

The last step is to override the “stopProcedure” function. This function needs to call the destroy method of an object that you created. Whenever “shutdown” is called on a lighty.io object, this method will be triggered.

Override a stopProcedure method:

@Override
    protected boolean stopProcedure() {
        if (transportpceProvider != null) {
            transportpceProvider.close();
            LOG.info("TransportpceProvider closed");
        }
        if (olmProvider != null) {
            olmProvider.close();
            LOG.info("OlmProvider closed");
        }
        if (rendererProvider != null) {
            rendererProvider.close();
            LOG.info("RendererProvider stopped");
        }
        if (rendererProvider != null) {
            rendererProvider.close();
            LOG.info("RendererProvider stopped");
        }
        if (servicehandlerProvider != null) {
            servicehandlerProvider.close();
            LOG.info("ServicehandlerProvider stopped");
        }
        if (stubpceProvider != null) {
            stubpceProvider.close();
            LOG.info("StubpceProvider stopped");
        }
        if (stubrendererProvider != null) {
            stubrendererProvider.close();
            LOG.info("StubrendererProvider stopped");
        }

        return true;
    }

Shutdown method from abstract class
    public synchronized ListenableFuture<Boolean> shutdown() {
        if(!this.running) {
            LOG.warn("LightyModule {} is already shut down.", this.getClass().getSimpleName());
            return Futures.immediateFuture(Boolean.valueOf(true));
        } else {
            LOG.info("Submitted shutdown of LightyModule {}.", this.getClass().getSimpleName());
            ListenableFuture<Boolean> shutdownFuture = this.executorService.submit(() -> {
                synchronized(this) {
                    LOG.debug("Starting shutdown procedure of LightyModule {}.", this.getClass().getSimpleName());
                    boolean stopResult = this.stopProcedure();
                    this.shutdownLatch.countDown();
                    this.running = false;
                    LOG.info("LightyModule {} shutdown complete.", this.getClass().getSimpleName());
                    return Boolean.valueOf(stopResult);
                }
            });
            return !this.executorIsProvided?Futures.transform(shutdownFuture, (result) -> {
                LOG.debug("Shutdown default ExecutorService of LightyModule {}.", this.getClass().getSimpleName());
                this.executorService.shutdown();
                this.executorService = null;
                return Boolean.valueOf(true);
            }, MoreExecutors.directExecutor()):shutdownFuture;
        }
    }

Keep in mind that your controller has to have access to all YANG model artifacts that you are using in your project. If you fail to do so, schema context of controller will be missing them and therefore some of your bean objects will not work properly.

As you can see these steps are simple and straightforward. If all the ODL projects were this clean, a simple script would be able to take care of migration from ODL to lighty.io.

Categories: lighty.io