- [X] quit/shutdown shell
-- [ ] working deployment
+- [X] working deployment
- [X] working /diagstatus and showSvcStatus (#39) [waiting for infrautils merges]
+- [ ] RestConfWiring with Web API
+
+- [ ] make RestConfConfig readable from YAML using http://immutables.github.io/json.html and update @ConfigImmutableStyle for it, then upstream to infrautils
+
- [ ] ditch AAA and use Filter from Jetty for BASIC auth instead
-- [ ] CDS instead of in-memory DS: [CONTROLLER-1831](https://jira.opendaylight.org/browse/CONTROLLER-1831)
+- [ ] NeutronWiring with Web API (req. by netvirt, but not genius)
-- [ ] RestConfWiring with Web API
+- [ ] ovsdb https://github.com/vorburger/opendaylight-simple/issues/47
+
+- [ ] CDS instead of in-memory DS: [CONTROLLER-1831](https://jira.opendaylight.org/browse/CONTROLLER-1831)
- [X] OpenFlowPlugin wiring ConfigurationServiceFactoryImpl (OPNFLWPLUG-1037)
- [ ] OpenFlowPluginWiring PacketProcessingService <odl:action-provider>
+- [ ] OpenFlowPlugin wiring
-- [ ] skitt's https://github.com/vorburger/opendaylight-simple/issues/38
+- [X] skitt's https://github.com/vorburger/opendaylight-simple/issues/38
- [ ] DiagStatusWiring auto-discover ServiceStatusProvider
- [ ] CLI commands, such as ItmWiring's TepShowState and DiagStatusCommand in DiagStatusWiring, which `implements Action`, should be auto-discovered
- [ ] run genius CSIT
-- [ ] NeutronWiring with Web API (req. by netvirt, but not genius)
-
- [ ] do the same as this already did for genius for all of netvirt
- [ ] run netvirt CSIT
<type>pom</type>
<scope>import</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>restconf-artifacts</artifactId>
+ <version>1.9.0-SNAPSHOT</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
<!--
<dependency>
</dependencyManagement>
<dependencies>
+ <dependency>
+ <groupId>org.immutables</groupId>
+ <artifactId>value</artifactId>
+ </dependency>
<!-- TODO integrate neutron (after genius is working)
<dependency>
<groupId>org.opendaylight.neutron</groupId>
</exclusions>
-->
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>restconf-nb-rfc8040</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</exclusion>
- <!-- osgi.core overlaps (x5) with felix.configadmin, and we don't need it here anyway -->
+ <!-- osgi.core overlaps (x5) with felix.configadmin, and we don't need it here anyway
+ TODO EXCLUDE IT AGAIN LATER; FOR NOW, SOME CLASSES UNFORTUNATELY STILL REFERENCE IT
<exclusion>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</exclusion>
+ -->
<!-- osgi.compendium (big) overlaps (x12) with (small) felix.configadmin, and with org.osgi.service.event (x9)
<exclusion>
<groupId>org.osgi</groupId>
import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
import org.opendaylight.controller.md.sal.binding.test.DataBrokerTestModule;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
import org.opendaylight.infrautils.inject.guice.AbstractCloseableModule;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.broker.DOMRpcRouter;
import org.opendaylight.mdsal.simple.MdsalWiring;
@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
public class ControllerWiring extends AbstractCloseableModule {
+ // TODO rename this to InMemoryDataStoreModule - because that's what this really is
// TODO propose @Inject and @PreDestroy close() annotations at source to simplify this, a lot...
// TODO this is just for early stage POC! switch to real CDS wiring here, eventually..
DataBrokerTestModule dataBrokerTestModule = new DataBrokerTestModule(true);
DataBroker dataBroker = dataBrokerTestModule.getDataBroker();
+ DOMSchemaService domSchemaService = dataBrokerTestModule.getSchemaService();
+
+ bind(DOMSchemaService.class).toInstance(domSchemaService);
+ bind(DOMDataBroker.class).toInstance(dataBrokerTestModule.getDOMDataBroker());
bind(DataBroker.class).toInstance(dataBroker);
bindingToNormalizedNodeCodec = dataBrokerTestModule.getBindingToNormalizedNodeCodec();
domNotificationPublishService = dataBrokerTestModule.getDOMNotificationRouter();
+ bind(DOMNotificationService.class).toInstance(domNotificationPublishService);
+
bindingDOMNotificationPublishServiceAdapter = new BindingDOMNotificationPublishServiceAdapter(
bindingToNormalizedNodeCodec, domNotificationPublishService);
bind(NotificationPublishService.class).toInstance(bindingDOMNotificationPublishServiceAdapter);
bind(BindingNormalizedNodeSerializer.class).toInstance(bindingToNormalizedNodeCodec);
+
+ bind(DOMMountPointService.class).to(DOMMountPointServiceImpl.class);
+
+ DOMRpcRouter domRpcRouter = DOMRpcRouter.newInstance(domSchemaService);
+ bind(DOMRpcService.class).toInstance(domRpcRouter.getRpcService());
+
+ org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter controllerDOMRpcService
+ = new org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter(
+ domRpcRouter.getRpcService(), domRpcRouter.getRpcProviderService());
+ bind(org.opendaylight.controller.md.sal.dom.api.DOMRpcService.class).toInstance(controllerDOMRpcService);
+ bind(org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService.class)
+ .toInstance(controllerDOMRpcService);
}
@Override
bindingDOMNotificationPublishServiceAdapter.close();
domNotificationPublishService.close();
}
-
}
import org.opendaylight.infrautils.simple.InfraUtilsWiring;
import org.opendaylight.neutron.simple.NeutronModule;
import org.opendaylight.openflowplugin.simple.OpenFlowPluginWiring;
+import org.opendaylight.restconf.simple.RestConfModule;
import org.opendaylight.serviceutils.simple.ServiceUtilsWiring;
public class GeniusWiring extends AbstractModule {
// MD SAL
install(new ControllerWiring());
+ // RESTCONF
+ install(new RestConfModule());
+
// Daexim
// TODO write real DaeximWiring, and replace this line with an install(new DaeximWiring());
bind(DataImportBootReady.class).toInstance(new DataImportBootReady() {});
--- /dev/null
+/*
+ * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.infrautils.simple;
+
+import static org.immutables.value.Value.Style.ImplementationVisibility.PRIVATE;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.immutables.value.Value;
+
+/**
+ * <a href="http://immutables.org">Immutables.org</a> style meta annotation.
+ *
+ * @author Michael Vorburger.ch
+ */
+@Target({ElementType.PACKAGE, ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS) // Make it class retention for incremental compilation
+@Value.Style(
+ // generate *Builder as public top level type and the *Immutable impl. as private inner class
+ visibility = PRIVATE,
+ strictBuilder = true)
+public @interface ConfigImmutableStyle { }
+// Beware: Changes made here are not active without a restart in Eclipse (would need separate project)
// TODO read port from a -D parameter or configuration file instead of hard-coding
bind(WebServer.class).toInstance(new JettyWebServer(8181));
- // JSX-RS
+ // JAX-RS
bind(ServletSupport.class).to(JerseyServletSupport.class);
// TODO replace this NOOP WebContextSecurer with one with a fixed uid/pwd for HTTP BASIC (and ditch AAA)
--- /dev/null
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.simple;
+
+import java.net.InetAddress;
+import org.immutables.value.Value;
+import org.opendaylight.infrautils.simple.ConfigImmutableStyle;
+
+/**
+ * Configuration for the RESTCONF server.
+ *
+ * @author Michael Vorburger.ch
+ */
+@Value.Immutable
+@ConfigImmutableStyle
+public interface RestConfConfig {
+
+ static RestConfConfigBuilder builder() {
+ return new RestConfConfigBuilder();
+ }
+
+ /**
+ * IP interface which the WebSocket server will listen on.
+ */
+ // TODO make this Optional<InetAddress> and by default run on the same IF as the WebServer (TBD)
+ InetAddress webSocketAddress();
+
+ /**
+ * TCP port which the WebSocket server will listen on.
+ */
+ int webSocketPort();
+
+ /**
+ * Web URL prefix. Defaults to "/restconf".
+ */
+ default String contextPath() {
+ return "/restconf";
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.simple;
+
+import com.google.inject.AbstractModule;
+import java.net.InetAddress;
+
+/**
+ * Guice module for RestConf, based on {@link RestConfWiring}.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class RestConfModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(RestConfWiring.class);
+ bind(RestConfConfig.class).toInstance(RestConfConfig.builder()
+ // TODO webSocketAddress should be required, and read from WebServer (configurable in WebWiring)
+ .webSocketAddress(InetAddress.getLoopbackAddress())
+ // TODO webSocketPort should be read from some configuration file
+ .webSocketPort(9090).build());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.simple;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.core.Application;
+import org.opendaylight.aaa.web.ServletDetails;
+import org.opendaylight.aaa.web.WebContext;
+import org.opendaylight.aaa.web.WebContextRegistration;
+import org.opendaylight.aaa.web.WebServer;
+import org.opendaylight.aaa.web.servlet.ServletSupport;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfProviderImpl;
+import org.opendaylight.netconf.sal.restconf.impl.StatisticsRestconfServiceWrapper;
+import org.opendaylight.restconf.nb.rfc8040.RestconfApplication;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.services.wrapper.ServicesWrapper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Standalone wiring for RESTCONF.
+ *
+ * <p>ACK: Some lines here were originally inspired by the CommunityRestConf class
+ * from lighty.io. The differences include (1) that this class is "pure Java",
+ * because the intention here is only to incubate and then propose it upstream
+ * right into the netconf ODL project; (2) that we use infrautils.web (currently
+ * in aaa),
+ *
+ * @author Michael Vorburger.ch, partially based on code in lighty.io
+ */
+@Singleton
+public class RestConfWiring {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RestConfWiring.class);
+
+ // TODO upstream this into ODL netconf/restconf and replace its Blueprint XML with this
+
+ private final WebServer webServer;
+ private final WebContext webContext;
+ private final RestconfProviderImpl webSockerServer;
+ private WebContextRegistration webContextRegistration;
+
+ @Inject
+ public RestConfWiring(RestConfConfig config, WebServer webServer, ServletSupport jaxRS,
+ DOMSchemaService domSchemaService, DOMMountPointService domMountPointService, DOMRpcService domRpcService,
+ DOMDataBroker domDataBroker, DOMNotificationService domNotificationService) {
+ this.webServer = webServer;
+ LOG.info("config = {}", config);
+
+ // WebSocket
+ ControllerContext controllerContext = ControllerContext.newInstance(domSchemaService, domMountPointService,
+ domSchemaService);
+ BrokerFacade broker = BrokerFacade.newInstance(domRpcService, domDataBroker, domNotificationService,
+ controllerContext);
+ RestconfImpl restconf = RestconfImpl.newInstance(broker, controllerContext);
+ StatisticsRestconfServiceWrapper stats = StatisticsRestconfServiceWrapper.newInstance(restconf);
+ IpAddress wsIpAddress = IpAddressBuilder.getDefaultInstance(config.webSocketAddress().getHostAddress());
+ this.webSockerServer = new RestconfProviderImpl(stats, wsIpAddress, new PortNumber(config.webSocketPort()));
+
+ // Servlet
+ TransactionChainHandler transactionChainHandler = new TransactionChainHandler(domDataBroker);
+ SchemaContextHandler schemaCtxHandler = SchemaContextHandler.newInstance(transactionChainHandler,
+ domSchemaService);
+ schemaCtxHandler.init();
+ DOMMountPointServiceHandler domMountPointServiceHandler = DOMMountPointServiceHandler
+ .newInstance(domMountPointService);
+ DOMDataBrokerHandler domDataBrokerHandler = new DOMDataBrokerHandler(domDataBroker);
+ RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(domRpcService);
+ NotificationServiceHandler notificationServiceHandler = new NotificationServiceHandler(domNotificationService);
+ ServicesWrapper servicesWrapper = ServicesWrapper.newInstance(schemaCtxHandler, domMountPointServiceHandler,
+ transactionChainHandler, domDataBrokerHandler, rpcServiceHandler, notificationServiceHandler,
+ domSchemaService);
+
+ // This is currently hard-coded to DRAFT_18; if we ever actually need to support the
+ // older DRAFT_02 for anything, then (only) add it to the RestConfConfig and switch here
+ Application application = new RestconfApplication(schemaCtxHandler,
+ domMountPointServiceHandler, servicesWrapper);
+ HttpServlet servlet = jaxRS.createHttpServletBuilder(application).build();
+ this.webContext = WebContext.builder().contextPath(config.contextPath())
+ .addServlet(ServletDetails.builder().addUrlPattern("/*").servlet(servlet).build())
+ .build();
+
+ // TODO secure it, using web API
+ }
+
+ @PostConstruct
+ public void start() throws ServletException {
+ this.webContextRegistration = this.webServer.registerWebContext(webContext);
+ this.webSockerServer.start();
+ }
+
+ @PreDestroy
+ public void stop() {
+ this.webSockerServer.close();
+ if (webContextRegistration != null) {
+ this.webContextRegistration.close();
+ }
+ }
+}
public @Rule GuiceRule2 guice = new GuiceRule2(new GeniusWiring(CLASS_PATH_BINDER), new ShellTestWiring());
- @SuppressWarnings("unused")
- private @Inject InterfaceManagerService interfaceManagerService;
+ @Inject InterfaceManagerService interfaceManagerService;
- @SuppressWarnings("unused")
- private @Inject IITMProvider itmProvider;
+ @Inject IITMProvider itmProvider;
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.infrautils.inject.guice.testutils.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.opendaylight.infrautils.inject.guice.testutils.AnnotationsModule;
+import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule;
+import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule2;
+
+/**
+ * Test illustrating how to use the GuiceRule with an inline lamda binding.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class GuiceRuleLambdaTest {
+
+ public @Rule GuiceRule guice = new GuiceRule2(
+ binder -> binder.bind(String.class).toInstance("hello, world"),
+ new AnnotationsModule());
+
+ @Inject String string;
+
+ @Test public void testLambdaBinding() {
+ assertThat(string).isEqualTo("hello, world");
+ }
+}
@Test public void testDistribution() {
assertThat(closeableInjector).isNotNull();
}
-
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.simple.test;
+
+import java.io.IOException;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.opendaylight.aaa.web.WebServer;
+import org.opendaylight.controller.simple.ControllerWiring;
+import org.opendaylight.infrautils.inject.guice.testutils.AnnotationsModule;
+import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule;
+import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule2;
+import org.opendaylight.infrautils.simple.testutils.AbstractSimpleDistributionTest;
+import org.opendaylight.infrautils.testutils.TestHttpClient;
+import org.opendaylight.infrautils.web.WebWiring;
+import org.opendaylight.restconf.simple.RestConfModule;
+
+/**
+ * Tests if the {@link RestConfModule} works.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class RestConfModuleTest extends AbstractSimpleDistributionTest {
+
+ public @Rule GuiceRule guice = new GuiceRule2(
+ RestConfModule.class, ControllerWiring.class, WebWiring.class, AnnotationsModule.class);
+
+ @Inject WebServer webServer;
+ @Inject TestHttpClient http;
+
+ @Test public void testRestConf() throws IOException {
+ // TODO assertThat(http.responseCode(GET, "/restconf/modules/")).isEqualTo(200);
+
+ // TODO test security; add auth support to TestHttpClient, check that w.o. auth it's 401
+ }
+}