From e3de30b333789c898078896cab4928de76d18816 Mon Sep 17 00:00:00 2001 From: "tobias.pobocik" Date: Fri, 26 Jan 2024 15:33:09 +0100 Subject: [PATCH 1/2] Add notifications to CommunityRestConf Register servlet for notifications on /notif for both websockets and SSE in CommunityRestConf module. Note that lighty.io provides no configurable option to choose from websockets or SSE. SSE is hard-coded default. Signed-off-by: tobias.pobocik Signed-off-by: Ivan Hrasko --- .../rcgnmi/module/RcGnmiAppModule.java | 2 +- .../rnc/module/RncLightyModule.java | 1 + .../examples/controllers/bgpapp/Main.java | 1 + .../examples/community/aaa/restconf/Main.java | 1 + .../examples/controllers/actions/Main.java | 3 +- .../controllers/restconfapp/Main.java | 1 + .../netconf/tests/LightyTestUtils.java | 1 + .../lighty-restconf-nb-community/pom.xml | 4 ++ .../community/impl/CommunityRestConf.java | 37 +++++++++++++++++-- .../impl/CommunityRestConfBuilder.java | 15 +++++++- .../impl/util/RestConfConfigUtils.java | 5 ++- 11 files changed, 63 insertions(+), 8 deletions(-) diff --git a/lighty-applications/lighty-rcgnmi-app-aggregator/lighty-rcgnmi-app-module/src/main/java/io/lighty/applications/rcgnmi/module/RcGnmiAppModule.java b/lighty-applications/lighty-rcgnmi-app-aggregator/lighty-rcgnmi-app-module/src/main/java/io/lighty/applications/rcgnmi/module/RcGnmiAppModule.java index ec98a7bb8d..e7fa9171b8 100644 --- a/lighty-applications/lighty-rcgnmi-app-aggregator/lighty-rcgnmi-app-module/src/main/java/io/lighty/applications/rcgnmi/module/RcGnmiAppModule.java +++ b/lighty-applications/lighty-rcgnmi-app-aggregator/lighty-rcgnmi-app-module/src/main/java/io/lighty/applications/rcgnmi/module/RcGnmiAppModule.java @@ -110,7 +110,7 @@ private LightyController initController(final ControllerConfiguration config) th private CommunityRestConf initRestconf(final RestConfConfiguration config, final LightyServices services) { final RestConfConfiguration conf = RestConfConfigUtils.getRestConfConfiguration(config, services); - return CommunityRestConfBuilder.from(conf).build(); + return CommunityRestConfBuilder.from(conf).withScheduledThreadPool(services.getScheduledThreadPool()).build(); } private GnmiSouthboundModule initGnmiModule(final LightyServices services, diff --git a/lighty-applications/lighty-rnc-app-aggregator/lighty-rnc-module/src/main/java/io/lighty/applications/rnc/module/RncLightyModule.java b/lighty-applications/lighty-rnc-app-aggregator/lighty-rnc-module/src/main/java/io/lighty/applications/rnc/module/RncLightyModule.java index f3b3f1585e..dfba218522 100644 --- a/lighty-applications/lighty-rnc-app-aggregator/lighty-rnc-module/src/main/java/io/lighty/applications/rnc/module/RncLightyModule.java +++ b/lighty-applications/lighty-rnc-app-aggregator/lighty-rnc-module/src/main/java/io/lighty/applications/rnc/module/RncLightyModule.java @@ -131,6 +131,7 @@ private CommunityRestConf initRestconf(final RestConfConfiguration rcConfig, fin return CommunityRestConfBuilder.from(restConfConfiguration) .withLightyServer(jettyServerBuilder) + .withScheduledThreadPool(services.getScheduledThreadPool()) .build(); } diff --git a/lighty-examples/lighty-bgp-community-restconf-app/src/main/java/io/lighty/examples/controllers/bgpapp/Main.java b/lighty-examples/lighty-bgp-community-restconf-app/src/main/java/io/lighty/examples/controllers/bgpapp/Main.java index 05fa541cac..171688943d 100644 --- a/lighty-examples/lighty-bgp-community-restconf-app/src/main/java/io/lighty/examples/controllers/bgpapp/Main.java +++ b/lighty-examples/lighty-bgp-community-restconf-app/src/main/java/io/lighty/examples/controllers/bgpapp/Main.java @@ -111,6 +111,7 @@ public synchronized void start(String[] args) throws InterruptedException, Execu restconf = CommunityRestConfBuilder .from(RestConfConfigUtils.getRestConfConfiguration(restConfConfiguration, controller.getServices())) + .withScheduledThreadPool(controller.getServices().getScheduledThreadPool()) .build(); Preconditions.checkState(startLightyModule(restconf, modulesConfig.getModuleTimeoutSeconds()), "Unable to start restconf module"); diff --git a/lighty-examples/lighty-community-aaa-restconf-app/src/main/java/io/lighty/kit/examples/community/aaa/restconf/Main.java b/lighty-examples/lighty-community-aaa-restconf-app/src/main/java/io/lighty/kit/examples/community/aaa/restconf/Main.java index 9d0b4729ec..9fbfb32755 100644 --- a/lighty-examples/lighty-community-aaa-restconf-app/src/main/java/io/lighty/kit/examples/community/aaa/restconf/Main.java +++ b/lighty-examples/lighty-community-aaa-restconf-app/src/main/java/io/lighty/kit/examples/community/aaa/restconf/Main.java @@ -114,6 +114,7 @@ private void startLighty(final ControllerConfiguration controllerConfiguration, .from(RestConfConfigUtils.getRestConfConfiguration(restconfConfiguration, this.lightyController.getServices())) .withLightyServer(jettyServerBuilder) + .withScheduledThreadPool(this.lightyController.getServices().getScheduledThreadPool()) .build(); final boolean restconfStartOk = this.restconf.start() .get(modulesConfig.getModuleTimeoutSeconds(), TimeUnit.SECONDS); diff --git a/lighty-examples/lighty-community-restconf-actions-app/src/main/java/io/lighty/examples/controllers/actions/Main.java b/lighty-examples/lighty-community-restconf-actions-app/src/main/java/io/lighty/examples/controllers/actions/Main.java index 9b419469a8..7997d9e39c 100644 --- a/lighty-examples/lighty-community-restconf-actions-app/src/main/java/io/lighty/examples/controllers/actions/Main.java +++ b/lighty-examples/lighty-community-restconf-actions-app/src/main/java/io/lighty/examples/controllers/actions/Main.java @@ -68,7 +68,7 @@ public void start() { } @SuppressWarnings("IllegalCatch") - @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT") + @SuppressFBWarnings({"SLF4J_SIGN_ONLY_FORMAT", "REC_CATCH_EXCEPTION"}) public void start(String[] args, boolean registerShutdownHook) { final Stopwatch stopwatch = Stopwatch.createStarted(); LOG.info(".__ .__ .__ __ .__ _________________ _______"); @@ -158,6 +158,7 @@ private void startLighty(final ControllerConfiguration controllerConfiguration, .from(RestConfConfigUtils.getRestConfConfiguration(restconfConfiguration, this.lightyController.getServices())) .withLightyServer(jettyServerBuilder) + .withScheduledThreadPool(lightyController.getServices().getScheduledThreadPool()) .build(); //3. start openApi and RestConf server diff --git a/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java b/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java index f7d0dace2c..0ad9179faf 100644 --- a/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java +++ b/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java @@ -149,6 +149,7 @@ private void startLighty(final ControllerConfiguration controllerConfiguration, .from(RestConfConfigUtils.getRestConfConfiguration(restconfConfiguration, this.lightyController.getServices())) .withLightyServer(jettyServerBuilder) + .withScheduledThreadPool(lightyController.getServices().getScheduledThreadPool()) .build(); //3. start openApi and RestConf server diff --git a/lighty-modules/integration-tests/src/test/java/io/lighty/modules/southbound/netconf/tests/LightyTestUtils.java b/lighty-modules/integration-tests/src/test/java/io/lighty/modules/southbound/netconf/tests/LightyTestUtils.java index d4e1c36282..69e0e8b798 100644 --- a/lighty-modules/integration-tests/src/test/java/io/lighty/modules/southbound/netconf/tests/LightyTestUtils.java +++ b/lighty-modules/integration-tests/src/test/java/io/lighty/modules/southbound/netconf/tests/LightyTestUtils.java @@ -69,6 +69,7 @@ public static CommunityRestConf startRestconf(RestConfConfiguration restConfConf final CommunityRestConf communityRestConf = CommunityRestConfBuilder .from(RestConfConfigUtils.getRestConfConfiguration(restConfConfiguration, services)) + .withScheduledThreadPool(services.getScheduledThreadPool()) .build(); LOG.info("Starting CommunityRestConf"); diff --git a/lighty-modules/lighty-restconf-nb-community/pom.xml b/lighty-modules/lighty-restconf-nb-community/pom.xml index 76d4c6ee58..41bee89ccf 100644 --- a/lighty-modules/lighty-restconf-nb-community/pom.xml +++ b/lighty-modules/lighty-restconf-nb-community/pom.xml @@ -53,6 +53,10 @@ org.glassfish.jaxb jaxb-runtime + + org.glassfish.jersey.media + jersey-media-sse + diff --git a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java index af85335b9c..5caa6c40b7 100644 --- a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java +++ b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java @@ -7,6 +7,9 @@ */ package io.lighty.modules.northbound.restconf.community.impl; +import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.DATA_SUBSCRIPTION; +import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.NOTIFICATION_STREAM; + import com.google.common.base.Stopwatch; import com.google.common.base.Throwables; import io.lighty.core.controller.api.AbstractLightyModule; @@ -15,23 +18,30 @@ import io.lighty.server.LightyServerBuilder; import java.net.InetAddress; import java.net.InetSocketAddress; +import javax.servlet.ServletContext; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.websocket.server.WebSocketServerFactory; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.mdsal.dom.api.DOMNotificationService; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.restconf.nb.rfc8040.DataStreamApplication; import org.opendaylight.restconf.nb.rfc8040.RestconfApplication; import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider; import org.opendaylight.restconf.nb.rfc8040.databind.mdsal.DOMDatabindProvider; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; +import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataStreamServiceImpl; import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration; +import org.opendaylight.restconf.nb.rfc8040.streams.WebSocketInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,13 +61,14 @@ public class CommunityRestConf extends AbstractLightyModule { private Server jettyServer; private LightyServerBuilder lightyServerBuilder; private SchemaContextHandler schemaCtxHandler; + private final ScheduledThreadPool scheduledThreadPool; public CommunityRestConf(final DOMDataBroker domDataBroker, final DOMRpcService domRpcService, final DOMActionService domActionService, final DOMNotificationService domNotificationService, final DOMMountPointService domMountPointService, final DOMSchemaService domSchemaService, final InetAddress inetAddress, final int httpPort, final String restconfServletContextPath, - final LightyServerBuilder serverBuilder) { + final LightyServerBuilder serverBuilder, final ScheduledThreadPool threadPool) { this.domDataBroker = domDataBroker; this.domRpcService = domRpcService; this.domActionService = domActionService; @@ -68,16 +79,17 @@ public CommunityRestConf(final DOMDataBroker domDataBroker, final DOMRpcService this.httpPort = httpPort; this.inetAddress = inetAddress; this.restconfServletContextPath = restconfServletContextPath; + this.scheduledThreadPool = threadPool; } public CommunityRestConf(final DOMDataBroker domDataBroker, final DOMRpcService domRpcService, final DOMActionService domActionService, final DOMNotificationService domNotificationService, final DOMMountPointService domMountPointService, final DOMSchemaService domSchemaService, final InetAddress inetAddress, final int httpPort, - final String restconfServletContextPath) { + final String restconfServletContextPath, final ScheduledThreadPool threadPool) { this(domDataBroker, domRpcService, domActionService, domNotificationService, domMountPointService, domSchemaService, inetAddress, httpPort, - restconfServletContextPath, null); + restconfServletContextPath, null, threadPool); } @Override @@ -93,10 +105,16 @@ protected boolean initProcedure() { final RestconfApplication restconfApplication = new RestconfApplication(databindProvider, this.domMountPointService, this.domDataBroker, this.domRpcService, this.domActionService, this.domNotificationService, this.domSchemaService, streamsConfiguration); + final DataStreamApplication dataStreamApplication = new DataStreamApplication(databindProvider, + this.domMountPointService, new RestconfDataStreamServiceImpl(scheduledThreadPool, streamsConfiguration)); final ServletContainer servletContainer8040 = new ServletContainer(ResourceConfig .forApplication(restconfApplication)); final ServletHolder jaxrs = new ServletHolder(servletContainer8040); + final ServletContainer dataStreamServletContainer = new ServletContainer( + ResourceConfig.forApplication(dataStreamApplication)); + final ServletHolder dataStreamHolder = new ServletHolder(dataStreamServletContainer); + LOG.info("RestConf init complete, starting Jetty"); LOG.info("http address:port {}:{}, url prefix: {}", this.inetAddress.toString(), this.httpPort, this.restconfServletContextPath); @@ -107,6 +125,19 @@ protected boolean initProcedure() { final ServletContextHandler mainHandler = new ServletContextHandler(contexts, this.restconfServletContextPath, true, false); mainHandler.addServlet(jaxrs, "/*"); + mainHandler.addServlet(dataStreamHolder, "/notif/*"); + + final ServletContextHandler dataHandler = new ServletContextHandler( + contexts, "/" + DATA_SUBSCRIPTION, true, false); + final ServletContextHandler notifHandler = new ServletContextHandler( + contexts, "/" + NOTIFICATION_STREAM, true, false); + final WebSocketInitializer webSocketInitializer = new WebSocketInitializer( + scheduledThreadPool, streamsConfiguration); + final ServletContext context = notifHandler.getServletContext(); + final WebSocketServletFactory factory = new WebSocketServerFactory(context); + webSocketInitializer.configure(factory); + dataHandler.addServlet(new ServletHolder(webSocketInitializer), "/*"); + notifHandler.addServlet(new ServletHolder(webSocketInitializer), "/*"); final ServletContextHandler rrdHandler = new ServletContextHandler(contexts, "/.well-known", true, false); diff --git a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConfBuilder.java b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConfBuilder.java index 990a63df30..7c9fb2c683 100644 --- a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConfBuilder.java +++ b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConfBuilder.java @@ -9,6 +9,7 @@ import io.lighty.modules.northbound.restconf.community.impl.config.RestConfConfiguration; import io.lighty.server.LightyServerBuilder; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; /** * Builder for {@link CommunityRestConf}. @@ -17,6 +18,7 @@ public final class CommunityRestConfBuilder { private RestConfConfiguration restconfConfiguration = null; private LightyServerBuilder lightyServerBuilder = null; + private ScheduledThreadPool threadPool = null; private CommunityRestConfBuilder(final RestConfConfiguration configuration) { @@ -44,6 +46,17 @@ public CommunityRestConfBuilder withLightyServer(final LightyServerBuilder serve return this; } + /** + * Add ScheduledThreadPool. + * + * @param pool input scheduledThreadPool. + * @return instance of {@link CommunityRestConfBuilder}. + */ + public CommunityRestConfBuilder withScheduledThreadPool(final ScheduledThreadPool pool) { + this.threadPool = pool; + return this; + } + /** * Build new {@link CommunityRestConf} instance from {@link CommunityRestConfBuilder}. * @return instance of CommunityRestConf. @@ -55,6 +68,6 @@ public CommunityRestConf build() { this.restconfConfiguration.getDomMountPointService(), this.restconfConfiguration.getDomSchemaService(), this.restconfConfiguration.getInetAddress(), this.restconfConfiguration.getHttpPort(), - this.restconfConfiguration.getRestconfServletContextPath(), this.lightyServerBuilder); + this.restconfConfiguration.getRestconfServletContextPath(), this.lightyServerBuilder, this.threadPool); } } diff --git a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/util/RestConfConfigUtils.java b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/util/RestConfConfigUtils.java index 70f57da15a..b6021d0607 100644 --- a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/util/RestConfConfigUtils.java +++ b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/util/RestConfConfigUtils.java @@ -40,8 +40,9 @@ public final class RestConfConfigUtils { org.opendaylight.yang.gen.v1.subscribe.to.notification.rev161028 .$YangModuleInfoImpl.getInstance(), org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121 - .$YangModuleInfoImpl.getInstance() - ); + .$YangModuleInfoImpl.getInstance(), + org.opendaylight.yang.gen.v1.urn.opendaylight.device.notification.rev221106 + .$YangModuleInfoImpl.getInstance()); public static final int MAXIMUM_FRAGMENT_LENGTH = 0; public static final int IDLE_TIMEOUT = 30000; public static final int HEARTBEAT_INTERVAL = 10000; From 702fbccd7b153e49e219b4153513d3a685b24f96 Mon Sep 17 00:00:00 2001 From: "tobias.pobocik" Date: Mon, 29 Jan 2024 11:09:41 +0100 Subject: [PATCH 2/2] Create a workaround for NETCONF-1218 Because base path is hard-coded in ODL, we need to make this application use /rests base. This commit should be reverted after NETCONF-1218 is resolved and implemented. Signed-off-by: tobias.pobocik Signed-off-by: Ivan Hrasko --- .../lighty/examples/controllers/restconfapp/Main.java | 4 ++++ .../controllers/restconfapp/tests/RestconfAppTest.java | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java b/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java index 0ad9179faf..8d75f7228d 100644 --- a/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java +++ b/lighty-examples/lighty-community-restconf-netconf-app/src/main/java/io/lighty/examples/controllers/restconfapp/Main.java @@ -133,6 +133,10 @@ private void startLighty(final ControllerConfiguration controllerConfiguration, throws ConfigurationException, ExecutionException, InterruptedException, TimeoutException, ModuleStartupException { + //FIXME remove this after paths NETCONF-1218 is resolved. + restconfConfiguration.setRestconfServletContextPath("/rests"); + restconfConfiguration.setHttpPort(8181); + //1. initialize and start Lighty controller (MD-SAL, Controller, YangTools, Akka) LightyControllerBuilder lightyControllerBuilder = new LightyControllerBuilder(); this.lightyController = lightyControllerBuilder.from(controllerConfiguration).build(); diff --git a/lighty-examples/lighty-community-restconf-netconf-app/src/test/java/io/lighty/examples/controllers/restconfapp/tests/RestconfAppTest.java b/lighty-examples/lighty-community-restconf-netconf-app/src/test/java/io/lighty/examples/controllers/restconfapp/tests/RestconfAppTest.java index 880bdc0ed3..3a295516e1 100644 --- a/lighty-examples/lighty-community-restconf-netconf-app/src/test/java/io/lighty/examples/controllers/restconfapp/tests/RestconfAppTest.java +++ b/lighty-examples/lighty-community-restconf-netconf-app/src/test/java/io/lighty/examples/controllers/restconfapp/tests/RestconfAppTest.java @@ -37,7 +37,7 @@ public class RestconfAppTest { public static void init() { restconfApp = new Main(); restconfApp.start(); - restClient = new RestClient("http://localhost:8888/"); + restClient = new RestClient("http://localhost:8181/"); } /** @@ -46,7 +46,7 @@ public static void init() { @Test public void simpleApplicationTest() throws IOException, InterruptedException { HttpResponse operations; - restClient.POST("restconf/data/network-topology:network-topology/topology=topology-netconf", + restClient.POST("rests/data/network-topology:network-topology/topology=topology-netconf", """ { "netconf-topology:node": [ @@ -56,11 +56,11 @@ public void simpleApplicationTest() throws IOException, InterruptedException { ] }"""); - operations = restClient.GET("restconf/operations"); + operations = restClient.GET("rests/operations"); Assert.assertEquals(operations.statusCode(), 200); - operations = restClient.GET("restconf/data/network-topology:network-topology?content=config"); + operations = restClient.GET("rests/data/network-topology:network-topology?content=config"); Assert.assertEquals(operations.statusCode(), 200); - operations = restClient.GET("restconf/data/network-topology:network-topology?content=nonconfig"); + operations = restClient.GET("rests/data/network-topology:network-topology?content=nonconfig"); Assert.assertEquals(operations.statusCode(), 200); }