From: Ed Warnicke Date: Fri, 6 Jun 2014 12:21:03 +0000 (+0000) Subject: Merge "Bug 1073: Added Transaction Chain support to InMemoryDataTreeModification." X-Git-Tag: release/helium~689 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=2c4a9be1a89caa93f4f2697401771dcb0fa67c5a;hp=f7e2efa41f9c6be3282204c72817490b1e6b2cb2 Merge "Bug 1073: Added Transaction Chain support to InMemoryDataTreeModification." --- diff --git a/features/base/src/main/resources/features.xml b/features/base/src/main/resources/features.xml index cc112052cc..23051f5a9a 100644 --- a/features/base/src/main/resources/features.xml +++ b/features/base/src/main/resources/features.xml @@ -1,10 +1,7 @@ - + - + http transaction base-felix-dm @@ -21,41 +18,46 @@ mvn:org.opendaylight.controller/dummy-console/1.1.0-SNAPSHOT - + mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version} mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version} mvn:org.apache.felix/org.apache.felix.dependencymanager.shell/${felix.dependencymanager.shell.version} - + mvn:org.apache.aries/org.apache.aries.util/1.1.0 mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/${spifly.version} mvn:org.ow2.asm/asm-all/4.0 - - wrap:mvn:io.netty/netty-buffer/${netty.version} - wrap:mvn:io.netty/netty-codec/${netty.version} - wrap:mvn:io.netty/netty-transport/${netty.version} - wrap:mvn:io.netty/netty-common/${netty.version} - wrap:mvn:io.netty/netty-handler/${netty.version} - wrap:mvn:io.netty/netty-codec-http/${netty.version} - mvn:org.opendaylight.controller.thirdparty/ganymed/1.1-SNAPSHOT - - - base-gemini-web - mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version} - mvn:com.sun.jersey/jersey-server/${jersey.version} - mvn:com.sun.jersey/jersey-core/${jersey.version} - mvn:com.sun.jersey/jersey-client/${jersey.version} - mvn:com.sun.jersey/jersey-servlet/${jersey.version} + + wrap:mvn:io.netty/netty-buffer/${netty.version} + wrap:mvn:io.netty/netty-codec/${netty.version} + wrap:mvn:io.netty/netty-transport/${netty.version} + wrap:mvn:io.netty/netty-common/${netty.version} + wrap:mvn:io.netty/netty-handler/${netty.version} + wrap:mvn:io.netty/netty-codec-http/${netty.version} + mvn:org.opendaylight.controller.thirdparty/ganymed/1.1-SNAPSHOT + + + base-gemini-web + mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version} + mvn:com.sun.jersey/jersey-server/${jersey.version} + mvn:com.sun.jersey/jersey-core/${jersey.version} + mvn:com.sun.jersey/jersey-client/${jersey.version} + mvn:com.sun.jersey/jersey-servlet/${jersey.version} + mvn:javax.ws.rs/javax.ws.rs-api/2.0 + + http + mvn:com.eclipsesource.jaxrs/jersey-all/${jersey2.version} + mvn:com.eclipsesource.jaxrs/publisher/${jersey2.publisher.version} + mvn:javax.ws.rs/javax.ws.rs-api/${jsr311.v2.api.version} + mvn:javax.annotation/javax.annotation-api/${javax.annotation.version} + mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version} mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version} mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version} mvn:org.codehaus.jettison/jettison/${jettison.version} - mvn:javax.ws.rs/jsr311-api/${jsr311.api.version} mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version} mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} @@ -66,8 +68,7 @@ mvn:org.slf4j/slf4j-simple/1.7.2 mvn:org.slf4j/slf4j-api/1.7.2 - + mvn:com.google.guava/guava/${guava.version} mvn:org.javassist/javassist/${javassist.version} mvn:commons-io/commons-io/${commons.io.version} diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index c166f668cc..077b452f0a 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -88,14 +88,18 @@ 1.7 1.7 3.17.1-GA + 1.2 1.17 1.17 + 4.0 + 2.8 1.3.3 src/main/yang-gen-config 1.1.4 2.0.1 1.1.1 + 2.0 4.8.1 3.0.1 1.0.9 @@ -107,7 +111,7 @@ 1.1-SNAPSHOT 1.9.5 0.2.5-SNAPSHOT - 4.0.17.Final + 4.0.19.Final 0.0.3-SNAPSHOT 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT @@ -946,6 +950,11 @@ ${netconf.version} test-jar + + org.opendaylight.controller + netconf-tcp + ${netconf.version} + org.opendaylight.controller netconf-util diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java index 916ef9a88b..fef2c71969 100644 --- a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java @@ -7,12 +7,16 @@ */ package org.opendaylight.protocol.framework; +import com.google.common.base.Preconditions; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; +import io.netty.channel.ServerChannel; +import io.netty.channel.local.LocalServerChannel; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; @@ -21,22 +25,20 @@ import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.Promise; - import java.io.Closeable; import java.net.InetSocketAddress; - +import java.net.SocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; - /** * Dispatcher class for creating servers and clients. The idea is to first create servers and clients and the run the * start method that will handle sockets in different thread. */ public abstract class AbstractDispatcher, L extends SessionListener> implements Closeable { - protected interface PipelineInitializer> { + + protected interface ChannelPipelineInitializer> { /** * Initializes channel by specifying the handlers in its pipeline. Handlers are protocol specific, therefore this * method needs to be implemented in protocol specific Dispatchers. @@ -44,7 +46,11 @@ public abstract class AbstractDispatcher, L extends * @param channel whose pipeline should be defined, also to be passed to {@link SessionNegotiatorFactory} * @param promise to be passed to {@link SessionNegotiatorFactory} */ - void initializeChannel(SocketChannel channel, Promise promise); + void initializeChannel(CH channel, Promise promise); + } + + protected interface PipelineInitializer> extends ChannelPipelineInitializer { + } @@ -76,25 +82,43 @@ public abstract class AbstractDispatcher, L extends * @return ChannelFuture representing the binding process */ protected ChannelFuture createServer(final InetSocketAddress address, final PipelineInitializer initializer) { + return createServer(address, NioServerSocketChannel.class, initializer); + } + + /** + * Creates server. Each server needs factories to pass their instances to client sessions. + * + * @param address address to which the server should be bound + * @param channelClass The {@link Class} which is used to create {@link Channel} instances from. + * @param initializer instance of PipelineInitializer used to initialize the channel pipeline + * + * @return ChannelFuture representing the binding process + */ + protected ChannelFuture createServer(SocketAddress address, Class channelClass, + final ChannelPipelineInitializer initializer) { final ServerBootstrap b = new ServerBootstrap(); - b.childHandler(new ChannelInitializer() { + b.childHandler(new ChannelInitializer() { @Override - protected void initChannel(final SocketChannel ch) { + protected void initChannel(final CH ch) { initializer.initializeChannel(ch, new DefaultPromise(executor)); } }); b.option(ChannelOption.SO_BACKLOG, 128); - b.childOption(ChannelOption.SO_KEEPALIVE, true); + if (LocalServerChannel.class.equals(channelClass) == false) { + // makes no sense for LocalServer and produces warning + b.childOption(ChannelOption.SO_KEEPALIVE, true); + } customizeBootstrap(b); if (b.group() == null) { b.group(bossGroup, workerGroup); } try { - b.channel(NioServerSocketChannel.class); + b.channel(channelClass); } catch (IllegalStateException e) { + // FIXME: if this is ok, document why LOG.trace("Not overriding channelFactory on bootstrap {}", b, e); } diff --git a/opendaylight/config/logback-config-loader/pom.xml b/opendaylight/config/logback-config-loader/pom.xml new file mode 100644 index 0000000000..03ff65f662 --- /dev/null +++ b/opendaylight/config/logback-config-loader/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + org.opendaylight.controller + config-plugin-parent + 0.2.5-SNAPSHOT + ../config-plugin-parent + + logback-config-loader + bundle + ${project.artifactId} + + 3.0.4 + + + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + org.slf4j + slf4j-api + + + + + junit + junit + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.logback.config.loader.Activator + + + + + + diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java new file mode 100644 index 0000000000..99866d5767 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader; + +import java.io.File; +import java.util.List; + +import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigUtil; +import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigurationLoader; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * default activator for loading multiple logback configuration files + */ +public class Activator implements BundleActivator { + + /** + * expected environment variable name, containing the root folder containing + * logback configurations + */ + private static final String LOGBACK_CONFIG_D = "logback.config.d"; + private static Logger LOG = LoggerFactory.getLogger(Activator.class); + + @Override + public void start(BundleContext context) { + LOG.info("Starting logback configuration loader"); + String logbackConfigRoot = System.getProperty(LOGBACK_CONFIG_D); + LOG.debug("configRoot: {}", logbackConfigRoot); + if (logbackConfigRoot != null) { + File logbackConfigRootFile = new File(logbackConfigRoot); + List sortedConfigFiles = LogbackConfigUtil.harvestSortedConfigFiles(logbackConfigRootFile); + LogbackConfigurationLoader.load(true, sortedConfigFiles.toArray()); + } + } + + @Override + public void stop(BundleContext context) { + LOG.info("Stopping logback configuration loader"); + // TODO: need reset/reload default config? + } + +} diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java new file mode 100644 index 0000000000..ddf14d7dd3 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.impl; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * logback config utils + */ +public final class LogbackConfigUtil { + + /** logback config file pattern (*.xml) */ + protected static final String LOGBACK_CONFIG_FILE_REGEX_SEED = ".+\\.xml"; + private static final Logger LOG = LoggerFactory + .getLogger(LogbackConfigUtil.class); + + /** + * forbidden ctor + */ + private LogbackConfigUtil() { + throw new UnsupportedOperationException(); + } + + /** + * @param logConfigRoot folder containing configuration files + * @return sorted list of found files + */ + public static List harvestSortedConfigFiles(File logConfigRoot) { + final Pattern xmlFilePattern = Pattern.compile(LOGBACK_CONFIG_FILE_REGEX_SEED); + File[] configs = logConfigRoot.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isFile() + && xmlFilePattern.matcher(pathname.getName()).find(); + } + }); + + List sortedConfigFiles = new ArrayList(configs.length); + for (File cfgItem : configs) { + LOG.trace("config: {}", cfgItem.toURI()); + sortedConfigFiles.add(cfgItem); + } + Collections.sort(sortedConfigFiles); + + return sortedConfigFiles; + } + +} diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java new file mode 100644 index 0000000000..2aa6b1a348 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 201 Cisco Systems, 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.controller.logback.config.loader.impl; + +import java.io.File; +import java.net.URL; + +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.util.StatusPrinter; + +/** + * Logback configuration loader. + * Strategy: + *
    + *
  1. reset actual configuration (probably default configuration)
  2. + *
  3. load all given logback config xml files in given order
  4. + *
+ */ +public final class LogbackConfigurationLoader { + + private static final Logger LOG = LoggerFactory + .getLogger(LogbackConfigurationLoader.class); + + /** + * forbidden ctor + */ + private LogbackConfigurationLoader() { + throw new UnsupportedOperationException(); + } + + /** + * load given logback configurations in given order, reset existing configuration before applying first one + * @param purgeBefore require reset before loading first config + * @param args + */ + public static void load(boolean purgeBefore, Object...args) { + try { + if (purgeBefore) { + resetExistingConfiguration(); + } + for (Object logbackConfig : args) { + load(logbackConfig); + } + } catch (IllegalStateException e) { + LOG.warn("loading of multiple logback configurations failed", e); + } + } + + /** + * purge existing logback configuration + */ + public static void resetExistingConfiguration() { + LOG.trace("resetting existing logback configuration"); + LoggerContext context = getLoggerContext(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + context.reset(); + } + + /** + * @return logback context + */ + private static LoggerContext getLoggerContext() { + ILoggerFactory context = LoggerFactory.getILoggerFactory(); + if (context != null && context instanceof LoggerContext) { + // now SLF4J is bound to logback in the current environment + return (LoggerContext) context; + } + throw new IllegalStateException("current logger factory is not supported: " + context); + } + + /** + * @param logbackConfig + * @param reset true if previous configuration needs to get purged + */ + public static void load(Object logbackConfig) { + LOG.trace("BEFORE logback reconfig"); + try { + LoggerContext context = getLoggerContext(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + if (logbackConfig instanceof String) { + configurator.doConfigure((String) logbackConfig); + } else if (logbackConfig instanceof URL) { + configurator.doConfigure((URL) logbackConfig); + } else if (logbackConfig instanceof File) { + configurator.doConfigure((File) logbackConfig); + } + + LOG.trace("applied {}", logbackConfig); + StatusPrinter.printInCaseOfErrorsOrWarnings(context); + } catch (IllegalStateException | JoranException je) { + LOG.warn("Logback configuration loading failed: {}", logbackConfig); + } + LOG.trace("AFTER logback reconfig"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java new file mode 100644 index 0000000000..2e9bf1df00 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigUtil; +import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigurationLoader; +import org.opendaylight.controller.logback.config.loader.test.logwork.Debugger; +import org.opendaylight.controller.logback.config.loader.test.logwork.Errorer; +import org.opendaylight.controller.logback.config.loader.test.logwork.Informer; +import org.opendaylight.controller.logback.config.loader.test.logwork.Tracer; +import org.opendaylight.controller.logback.config.loader.test.logwork.Warner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * test of logging config loader - {@link LogbackConfigurationLoader} + */ +@RunWith(JUnit4.class) +public class LogbackConfigurationLoaderTest { + + /** logback config root */ + private static final String LOGBACK_D = "/logback.d"; + private static Logger LOG = LoggerFactory + .getLogger(LogbackConfigurationLoaderTest.class); + + /** + * Test of method {@link LogbackConfigurationLoader#load(boolean, Object[])} + * + * @throws Exception + */ + @Test + public void testLoad() throws Exception { + File logConfigRoot = new File(LogbackConfigurationLoaderTest.class + .getResource(LOGBACK_D).getFile()); + List sortedConfigFiles = LogbackConfigUtil.harvestSortedConfigFiles(logConfigRoot); + LogbackConfigurationLoader.load(true, sortedConfigFiles.toArray()); + + LOG.info("LOGBACK ready -> about to use it"); + + Tracer.doSomeAction(); + Debugger.doSomeAction(); + Informer.doSomeAction(); + Warner.doSomeAction(); + Errorer.doSomeAction(); + + // check logs + String[] expectedLogs = new String[] { + "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.LogbackConfigurationLoaderTest: LOGBACK ready -> about to use it", + "LoggingEvent -> [TRACE] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: tracing", + "LoggingEvent -> [DEBUG] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: debugging", + "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: infoing", + "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: warning", + "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: erroring", + "LoggingEvent -> [DEBUG] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: debugging", + "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: infoing", + "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: warning", + "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: erroring", + "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: infoing", + "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: warning", + "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: erroring", + "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Warner: warning", + "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Warner: erroring", + "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Errorer: erroring" + + }; + + List logSnapshot = new ArrayList<>(TestAppender.getLogRecord()); + for (String logLine : logSnapshot) { + LOG.info("\"{}\",", logLine); + } + + Assert.assertArrayEquals(expectedLogs, logSnapshot.toArray()); + } +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java new file mode 100644 index 0000000000..b273d2777c --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.LogbackException; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import ch.qos.logback.core.status.Status; + +/** + * dummy appender for collecting log messages + * + * @param + */ +public class TestAppender implements Appender { + + private boolean started; + private Context context; + private String name; + + private static List logRecord = new ArrayList<>(); + + @Override + public void start() { + started = true; + } + + @Override + public void stop() { + started = false; + } + + @Override + public boolean isStarted() { + return started; + } + + @Override + public void setContext(Context context) { + this.context = context; + } + + @Override + public Context getContext() { + return context; + } + + @Override + public void addStatus(Status status) { + // TODO Auto-generated method stub + } + + @Override + public void addInfo(String msg) { + // TODO Auto-generated method stub + } + + @Override + public void addInfo(String msg, Throwable ex) { + // TODO Auto-generated method stub + } + + @Override + public void addWarn(String msg) { + // TODO Auto-generated method stub + } + + @Override + public void addWarn(String msg, Throwable ex) { + // TODO Auto-generated method stub + } + + @Override + public void addError(String msg) { + // TODO Auto-generated method stub + } + + @Override + public void addError(String msg, Throwable ex) { + // TODO Auto-generated method stub + } + + @Override + public void addFilter(Filter newFilter) { + // TODO Auto-generated method stub + } + + @Override + public void clearAllFilters() { + // TODO Auto-generated method stub + } + + @Override + public List> getCopyOfAttachedFiltersList() { + // TODO Auto-generated method stub + return null; + } + + @Override + public FilterReply getFilterChainDecision(E event) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public void doAppend(E event) throws LogbackException { + if (event instanceof LoggingEvent) { + LoggingEvent lEvent = (LoggingEvent) event; + logRecord.add(String.format("%s -> [%s] %s: %s", event.getClass() + .getSimpleName(), lEvent.getLevel(), + lEvent.getLoggerName(), lEvent.getMessage())); + } else { + logRecord.add(event.getClass() + " -> " + event.toString()); + } + } + + @Override + public void setName(String name) { + this.name = name; + } + + /** + * @return the logRecord + */ + public static List getLogRecord() { + return logRecord; + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java new file mode 100644 index 0000000000..a8052f71c9 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test.logwork; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * dummy logging guy + */ +public class Debugger { + + private static Logger LOG = LoggerFactory.getLogger(Debugger.class); + + /** + * all logging + */ + public static void doSomeAction() { + LOG.trace("tracing"); + LOG.debug("debugging"); + LOG.info("infoing"); + LOG.warn("warning"); + LOG.error("erroring"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java new file mode 100644 index 0000000000..0bcd830ad1 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test.logwork; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * dummy logging guy + */ +public class Errorer { + + private static Logger LOG = LoggerFactory.getLogger(Errorer.class); + + /** + * all logging + */ + public static void doSomeAction() { + LOG.trace("tracing"); + LOG.debug("debugging"); + LOG.info("infoing"); + LOG.warn("warning"); + LOG.error("erroring"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java new file mode 100644 index 0000000000..44f09315cf --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test.logwork; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * dummy logging guy + */ +public class Informer { + + private static Logger LOG = LoggerFactory.getLogger(Informer.class); + + /** + * all logging + */ + public static void doSomeAction() { + LOG.trace("tracing"); + LOG.debug("debugging"); + LOG.info("infoing"); + LOG.warn("warning"); + LOG.error("erroring"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java new file mode 100644 index 0000000000..70df607d8d --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test.logwork; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * dummy logging guy + */ +public class Tracer { + + private static Logger LOG = LoggerFactory.getLogger(Tracer.class); + + /** + * all logging + */ + public static void doSomeAction() { + LOG.trace("tracing"); + LOG.debug("debugging"); + LOG.info("infoing"); + LOG.warn("warning"); + LOG.error("erroring"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java new file mode 100644 index 0000000000..8093180a8b --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.logback.config.loader.test.logwork; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * dummy logging guy + */ +public class Warner { + + private static Logger LOG = LoggerFactory.getLogger(Warner.class); + + /** + * all logging + */ + public static void doSomeAction() { + LOG.trace("tracing"); + LOG.debug("debugging"); + LOG.info("infoing"); + LOG.warn("warning"); + LOG.error("erroring"); + } + +} diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml new file mode 100755 index 0000000000..7fb760aca7 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml new file mode 100755 index 0000000000..ca489d5c78 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml @@ -0,0 +1,19 @@ + + + + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml new file mode 100755 index 0000000000..89f82c5046 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml new file mode 100755 index 0000000000..a37b6f7f21 --- /dev/null +++ b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java index d9c9dada62..75323d256e 100644 --- a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java +++ b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java @@ -7,6 +7,14 @@ */ package org.opendaylight.controller.config.yang.logback.config; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.management.ObjectName; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -16,15 +24,6 @@ import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; -import javax.management.ObjectName; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.containsString; - public class LogbackModuleTest extends AbstractConfigTest { private static final String INSTANCE_NAME = "singleton"; @@ -89,7 +88,7 @@ public class LogbackModuleTest extends AbstractConfigTest { assertBeanCount(1, factory.getImplementationName()); ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - transaction.destroyConfigBean(factory.getImplementationName(), INSTANCE_NAME); + transaction.destroyModule(factory.getImplementationName(), INSTANCE_NAME); CommitStatus status = transaction.commit(); assertBeanCount(0, factory.getImplementationName()); diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 613940144b..5f34a9aa82 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -152,6 +152,35 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.12 + + false + false + checkstyle-logging.xml + true + true + ${project.basedir} + **\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang + **\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/ + + + + org.opendaylight.yangtools + checkstyle-logging + ${yangtools.version} + + + + + + check + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/fixed/FixedThreadPoolConfigBeanTest.java b/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/fixed/FixedThreadPoolConfigBeanTest.java index c95661d9c9..62b295be8d 100644 --- a/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/fixed/FixedThreadPoolConfigBeanTest.java +++ b/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/fixed/FixedThreadPoolConfigBeanTest.java @@ -7,6 +7,19 @@ */ package org.opendaylight.controller.config.threadpool.fixed; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.ArrayList; +import java.util.List; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.config.api.ConflictingVersionException; @@ -19,16 +32,11 @@ import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFacto import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleMXBean; import org.opendaylight.controller.config.yang.threadpool.impl.fixed.FixedThreadPoolModuleFactory; import org.opendaylight.controller.config.yang.threadpool.impl.fixed.FixedThreadPoolModuleMXBean; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.InstanceNotFoundException; -import javax.management.ObjectName; - -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.containsString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { + private static final Logger logger = LoggerFactory.getLogger(FixedThreadPoolConfigBeanTest.class); private FixedThreadPoolModuleFactory factory; private final String nameInstance = "fixedInstance"; @@ -36,7 +44,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { @Before public void setUp() { factory = new FixedThreadPoolModuleFactory(); - super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,factory, + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, factory, new NamingThreadFactoryModuleFactory())); } @@ -44,7 +52,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException, ConflictingVersionException { ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - createFixed(transaction, nameInstance, 2); + createFixed(transaction, nameInstance, 2, nameInstance); transaction.validateConfig(); CommitStatus status = transaction.commit(); @@ -57,7 +65,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException, ValidationException { ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - createFixed(transaction, nameInstance, 4); + createFixed(transaction, nameInstance, 4, nameInstance); transaction.validateConfig(); transaction.commit(); @@ -75,12 +83,12 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { public void testNegative() throws ConflictingVersionException, ValidationException, InstanceAlreadyExistsException { ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - createFixed(transaction, nameInstance, 5); + createFixed(transaction, nameInstance, 5, nameInstance); transaction.commit(); transaction = configRegistryClient.createTransaction(); try { - createFixed(transaction, nameInstance, 0); + createFixed(transaction, nameInstance, 0, nameInstance); fail(); } catch (InstanceAlreadyExistsException e) { assertThat( @@ -89,26 +97,56 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { } } + private int countThreadsByPrefix(String prefix) { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + int result = 0; + List names = new ArrayList<>(); + for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(false, false)) { + names.add(threadInfo.getThreadName()); + if (threadInfo.getThreadName().startsWith(prefix)) { + result++; + } + } + logger.info("Current threads {}", names); + return result; + } + @Test public void testDestroy() throws InstanceAlreadyExistsException, ValidationException, ConflictingVersionException, - InstanceNotFoundException { + InstanceNotFoundException, InterruptedException { + + String prefix = org.apache.commons.lang3.RandomStringUtils.randomAlphabetic(10); + + int numberOfThreads = 100; + int threadCount1 = countThreadsByPrefix(prefix); + assertEquals(0, threadCount1); ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - createFixed(transaction, nameInstance, 1); + createFixed(transaction, nameInstance, numberOfThreads, prefix); transaction.commit(); + int threadCount2 = countThreadsByPrefix(prefix); + assertEquals(numberOfThreads, threadCount2); transaction = configRegistryClient.createTransaction(); - transaction.destroyConfigBean(factory.getImplementationName(), nameInstance); + transaction.destroyModule(factory.getImplementationName(), nameInstance); CommitStatus status = transaction.commit(); assertBeanCount(0, factory.getImplementationName()); assertStatus(status, 0, 0, 1); + + for (int i = 0; i < 60; i++) { + if (countThreadsByPrefix(prefix) == 0) { + return; + } + Thread.sleep(1000); + } + assertEquals(0, countThreadsByPrefix(prefix)); } @Test public void testValidationException() throws InstanceAlreadyExistsException { ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); - createFixed(transaction, nameInstance, -1); + createFixed(transaction, nameInstance, -1, nameInstance); try { transaction.validateConfig(); fail(); @@ -117,7 +155,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { } } - private ObjectName createFixed(ConfigTransactionJMXClient transaction, String name, int numberOfThreads) + private ObjectName createFixed(ConfigTransactionJMXClient transaction, String name, int numberOfThreads, String prefix) throws InstanceAlreadyExistsException { ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), name); FixedThreadPoolModuleMXBean mxBean = transaction.newMXBeanProxy(nameCreated, FixedThreadPoolModuleMXBean.class); @@ -126,7 +164,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest { ObjectName threadFactoryON = transaction.createModule(NamingThreadFactoryModuleFactory.NAME, "naming"); NamingThreadFactoryModuleMXBean namingThreadFactoryModuleMXBean = transaction.newMXBeanProxy(threadFactoryON, NamingThreadFactoryModuleMXBean.class); - namingThreadFactoryModuleMXBean.setNamePrefix("prefix"); + namingThreadFactoryModuleMXBean.setNamePrefix(prefix); mxBean.setThreadFactory(threadFactoryON); diff --git a/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/scheduled/ScheduledThreadPoolConfigBeanTest.java b/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/scheduled/ScheduledThreadPoolConfigBeanTest.java index ef06e43d2f..0fc2fd6eb3 100644 --- a/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/scheduled/ScheduledThreadPoolConfigBeanTest.java +++ b/opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/scheduled/ScheduledThreadPoolConfigBeanTest.java @@ -7,6 +7,15 @@ */ package org.opendaylight.controller.config.threadpool.scheduled; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.config.api.ConflictingVersionException; @@ -20,16 +29,6 @@ import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFacto import org.opendaylight.controller.config.yang.threadpool.impl.scheduled.ScheduledThreadPoolModuleFactory; import org.opendaylight.controller.config.yang.threadpool.impl.scheduled.ScheduledThreadPoolModuleMXBean; -import javax.management.InstanceAlreadyExistsException; -import javax.management.InstanceNotFoundException; -import javax.management.ObjectName; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.containsString; - public class ScheduledThreadPoolConfigBeanTest extends AbstractConfigTest { private ScheduledThreadPoolModuleFactory factory; @@ -103,7 +102,7 @@ public class ScheduledThreadPoolConfigBeanTest extends AbstractConfigTest { transaction.commit(); transaction = configRegistryClient.createTransaction(); - transaction.destroyConfigBean(factory.getImplementationName(), instanceName); + transaction.destroyModule(factory.getImplementationName(), instanceName); CommitStatus status = transaction.commit(); assertBeanCount(0, factory.getImplementationName()); diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties new file mode 100644 index 0000000000..e91da89970 --- /dev/null +++ b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties @@ -0,0 +1,503 @@ +################################################################################ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# +# Java platform package export properties. +# + +# Standard package set. Note that: +# - javax.transaction* is exported with a mandatory attribute +jre-1.6= \ + javax.accessibility, \ + javax.activation;version="1.1", \ + javax.activity, \ + javax.crypto, \ + javax.crypto.interfaces, \ + javax.crypto.spec, \ + javax.imageio, \ + javax.imageio.event, \ + javax.imageio.metadata, \ + javax.imageio.plugins.bmp, \ + javax.imageio.plugins.jpeg, \ + javax.imageio.spi, \ + javax.imageio.stream, \ + javax.jws, \ + javax.jws.soap, \ + javax.lang.model, \ + javax.lang.model.element, \ + javax.lang.model.type, \ + javax.lang.model.util, \ + javax.management, \ + javax.management.loading, \ + javax.management.modelmbean, \ + javax.management.monitor, \ + javax.management.openmbean, \ + javax.management.relation, \ + javax.management.remote, \ + javax.management.remote.rmi, \ + javax.management.timer, \ + javax.naming, \ + javax.naming.directory, \ + javax.naming.event, \ + javax.naming.ldap, \ + javax.naming.spi, \ + javax.net, \ + javax.net.ssl, \ + javax.print, \ + javax.print.attribute, \ + javax.print.attribute.standard, \ + javax.print.event, \ + javax.rmi, \ + javax.rmi.CORBA, \ + javax.rmi.ssl, \ + javax.script, \ + javax.security.auth, \ + javax.security.auth.callback, \ + javax.security.auth.kerberos, \ + javax.security.auth.login, \ + javax.security.auth.spi, \ + javax.security.auth.x500, \ + javax.security.cert, \ + javax.security.sasl, \ + javax.sound.midi, \ + javax.sound.midi.spi, \ + javax.sound.sampled, \ + javax.sound.sampled.spi, \ + javax.sql, \ + javax.sql.rowset, \ + javax.sql.rowset.serial, \ + javax.sql.rowset.spi, \ + javax.swing, \ + javax.swing.border, \ + javax.swing.colorchooser, \ + javax.swing.event, \ + javax.swing.filechooser, \ + javax.swing.plaf, \ + javax.swing.plaf.basic, \ + javax.swing.plaf.metal, \ + javax.swing.plaf.multi, \ + javax.swing.plaf.synth, \ + javax.swing.table, \ + javax.swing.text, \ + javax.swing.text.html, \ + javax.swing.text.html.parser, \ + javax.swing.text.rtf, \ + javax.swing.tree, \ + javax.swing.undo, \ + javax.tools, \ + javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \ + javax.xml, \ + javax.xml.bind;version="2.2.1", \ + javax.xml.bind.annotation;version="2.2.1", \ + javax.xml.bind.annotation.adapters;version="2.2.1", \ + javax.xml.bind.attachment;version="2.2.1", \ + javax.xml.bind.helpers;version="2.2.1", \ + javax.xml.bind.util;version="2.2.1", \ + javax.xml.crypto, \ + javax.xml.crypto.dom, \ + javax.xml.crypto.dsig, \ + javax.xml.crypto.dsig.dom, \ + javax.xml.crypto.dsig.keyinfo, \ + javax.xml.crypto.dsig.spec, \ + javax.xml.datatype, \ + javax.xml.namespace, \ + javax.xml.parsers, \ + javax.xml.soap;version="1.3", \ + javax.xml.stream;version="1.2", \ + javax.xml.stream.events;version="1.2", \ + javax.xml.stream.util;version="1.2", \ + javax.xml.transform, \ + javax.xml.transform.dom, \ + javax.xml.transform.sax, \ + javax.xml.transform.stax, \ + javax.xml.transform.stream, \ + javax.xml.validation, \ + javax.xml.ws;version="2.2", \ + javax.xml.ws.handler;version="2.2", \ + javax.xml.ws.handler.soap;version="2.2", \ + javax.xml.ws.http;version="2.2", \ + javax.xml.ws.soap;version="2.2", \ + javax.xml.ws.spi;version="2.2", \ + javax.xml.ws.wsaddressing;version="2.2", \ + javax.xml.ws.spi.http;version="2.2", \ + javax.xml.xpath, \ + org.ietf.jgss, \ + org.omg.CORBA, \ + org.omg.CORBA_2_3, \ + org.omg.CORBA_2_3.portable, \ + org.omg.CORBA.DynAnyPackage, \ + org.omg.CORBA.ORBPackage, \ + org.omg.CORBA.portable, \ + org.omg.CORBA.TypeCodePackage, \ + org.omg.CosNaming, \ + org.omg.CosNaming.NamingContextExtPackage, \ + org.omg.CosNaming.NamingContextPackage, \ + org.omg.Dynamic, \ + org.omg.DynamicAny, \ + org.omg.DynamicAny.DynAnyFactoryPackage, \ + org.omg.DynamicAny.DynAnyPackage, \ + org.omg.IOP, \ + org.omg.IOP.CodecFactoryPackage, \ + org.omg.IOP.CodecPackage, \ + org.omg.Messaging, \ + org.omg.PortableInterceptor, \ + org.omg.PortableInterceptor.ORBInitInfoPackage, \ + org.omg.PortableServer, \ + org.omg.PortableServer.CurrentPackage, \ + org.omg.PortableServer.POAManagerPackage, \ + org.omg.PortableServer.POAPackage, \ + org.omg.PortableServer.portable, \ + org.omg.PortableServer.ServantLocatorPackage, \ + org.omg.SendingContext, \ + org.omg.stub.java.rmi, \ + org.omg.stub.javax.management.remote.rmi, \ + org.w3c.dom, \ + org.w3c.dom.bootstrap, \ + org.w3c.dom.css, \ + org.w3c.dom.events, \ + org.w3c.dom.html, \ + org.w3c.dom.ls, \ + org.w3c.dom.ranges, \ + org.w3c.dom.stylesheets, \ + org.w3c.dom.traversal, \ + org.w3c.dom.views, \ + org.w3c.dom.xpath, \ + org.xml.sax, \ + org.xml.sax.ext, \ + org.xml.sax.helpers + +# Standard package set. Note that: +# - javax.transaction* is exported with a mandatory attribute +jre-1.7= \ + javax.accessibility, \ + javax.activation;version="1.1", \ + javax.activity, \ + javax.crypto, \ + javax.crypto.interfaces, \ + javax.crypto.spec, \ + javax.imageio, \ + javax.imageio.event, \ + javax.imageio.metadata, \ + javax.imageio.plugins.bmp, \ + javax.imageio.plugins.jpeg, \ + javax.imageio.spi, \ + javax.imageio.stream, \ + javax.jws, \ + javax.jws.soap, \ + javax.lang.model, \ + javax.lang.model.element, \ + javax.lang.model.type, \ + javax.lang.model.util, \ + javax.management, \ + javax.management.loading, \ + javax.management.modelmbean, \ + javax.management.monitor, \ + javax.management.openmbean, \ + javax.management.relation, \ + javax.management.remote, \ + javax.management.remote.rmi, \ + javax.management.timer, \ + javax.naming, \ + javax.naming.directory, \ + javax.naming.event, \ + javax.naming.ldap, \ + javax.naming.spi, \ + javax.net, \ + javax.net.ssl, \ + javax.print, \ + javax.print.attribute, \ + javax.print.attribute.standard, \ + javax.print.event, \ + javax.rmi, \ + javax.rmi.CORBA, \ + javax.rmi.ssl, \ + javax.script, \ + javax.security.auth, \ + javax.security.auth.callback, \ + javax.security.auth.kerberos, \ + javax.security.auth.login, \ + javax.security.auth.spi, \ + javax.security.auth.x500, \ + javax.security.cert, \ + javax.security.sasl, \ + javax.sound.midi, \ + javax.sound.midi.spi, \ + javax.sound.sampled, \ + javax.sound.sampled.spi, \ + javax.sql, \ + javax.sql.rowset, \ + javax.sql.rowset.serial, \ + javax.sql.rowset.spi, \ + javax.swing, \ + javax.swing.border, \ + javax.swing.colorchooser, \ + javax.swing.event, \ + javax.swing.filechooser, \ + javax.swing.plaf, \ + javax.swing.plaf.basic, \ + javax.swing.plaf.metal, \ + javax.swing.plaf.multi, \ + javax.swing.plaf.synth, \ + javax.swing.table, \ + javax.swing.text, \ + javax.swing.text.html, \ + javax.swing.text.html.parser, \ + javax.swing.text.rtf, \ + javax.swing.tree, \ + javax.swing.undo, \ + javax.tools, \ + javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \ + javax.xml, \ + javax.xml.bind;version="2.2.1", \ + javax.xml.bind.annotation;version="2.2.1", \ + javax.xml.bind.annotation.adapters;version="2.2.1", \ + javax.xml.bind.attachment;version="2.2.1", \ + javax.xml.bind.helpers;version="2.2.1", \ + javax.xml.bind.util;version="2.2.1", \ + javax.xml.crypto, \ + javax.xml.crypto.dom, \ + javax.xml.crypto.dsig, \ + javax.xml.crypto.dsig.dom, \ + javax.xml.crypto.dsig.keyinfo, \ + javax.xml.crypto.dsig.spec, \ + javax.xml.datatype, \ + javax.xml.namespace, \ + javax.xml.parsers, \ + javax.xml.soap;version="1.3", \ + javax.xml.stream;version="1.2", \ + javax.xml.stream.events;version="1.2", \ + javax.xml.stream.util;version="1.2", \ + javax.xml.transform, \ + javax.xml.transform.dom, \ + javax.xml.transform.sax, \ + javax.xml.transform.stax, \ + javax.xml.transform.stream, \ + javax.xml.validation, \ + javax.xml.ws;version="2.2", \ + javax.xml.ws.handler;version="2.2", \ + javax.xml.ws.handler.soap;version="2.2", \ + javax.xml.ws.http;version="2.2", \ + javax.xml.ws.soap;version="2.2", \ + javax.xml.ws.spi;version="2.2", \ + javax.xml.ws.wsaddressing;version="2.2", \ + javax.xml.ws.spi.http;version="2.2", \ + javax.xml.xpath, \ + org.ietf.jgss, \ + org.omg.CORBA, \ + org.omg.CORBA_2_3, \ + org.omg.CORBA_2_3.portable, \ + org.omg.CORBA.DynAnyPackage, \ + org.omg.CORBA.ORBPackage, \ + org.omg.CORBA.portable, \ + org.omg.CORBA.TypeCodePackage, \ + org.omg.CosNaming, \ + org.omg.CosNaming.NamingContextExtPackage, \ + org.omg.CosNaming.NamingContextPackage, \ + org.omg.Dynamic, \ + org.omg.DynamicAny, \ + org.omg.DynamicAny.DynAnyFactoryPackage, \ + org.omg.DynamicAny.DynAnyPackage, \ + org.omg.IOP, \ + org.omg.IOP.CodecFactoryPackage, \ + org.omg.IOP.CodecPackage, \ + org.omg.Messaging, \ + org.omg.PortableInterceptor, \ + org.omg.PortableInterceptor.ORBInitInfoPackage, \ + org.omg.PortableServer, \ + org.omg.PortableServer.CurrentPackage, \ + org.omg.PortableServer.POAManagerPackage, \ + org.omg.PortableServer.POAPackage, \ + org.omg.PortableServer.portable, \ + org.omg.PortableServer.ServantLocatorPackage, \ + org.omg.SendingContext, \ + org.omg.stub.java.rmi, \ + org.omg.stub.javax.management.remote.rmi, \ + org.w3c.dom, \ + org.w3c.dom.bootstrap, \ + org.w3c.dom.css, \ + org.w3c.dom.events, \ + org.w3c.dom.html, \ + org.w3c.dom.ls, \ + org.w3c.dom.ranges, \ + org.w3c.dom.stylesheets, \ + org.w3c.dom.traversal, \ + org.w3c.dom.views, \ + org.w3c.dom.xpath, \ + org.xml.sax, \ + org.xml.sax.ext, \ + org.xml.sax.helpers + +jre-1.8= \ + javax.accessibility, \ + javax.activation;version="1.1", \ + javax.activity, \ + javax.crypto, \ + javax.crypto.interfaces, \ + javax.crypto.spec, \ + javax.imageio, \ + javax.imageio.event, \ + javax.imageio.metadata, \ + javax.imageio.plugins.bmp, \ + javax.imageio.plugins.jpeg, \ + javax.imageio.spi, \ + javax.imageio.stream, \ + javax.jws, \ + javax.jws.soap, \ + javax.lang.model, \ + javax.lang.model.element, \ + javax.lang.model.type, \ + javax.lang.model.util, \ + javax.management, \ + javax.management.loading, \ + javax.management.modelmbean, \ + javax.management.monitor, \ + javax.management.openmbean, \ + javax.management.relation, \ + javax.management.remote, \ + javax.management.remote.rmi, \ + javax.management.timer, \ + javax.naming, \ + javax.naming.directory, \ + javax.naming.event, \ + javax.naming.ldap, \ + javax.naming.spi, \ + javax.net, \ + javax.net.ssl, \ + javax.print, \ + javax.print.attribute, \ + javax.print.attribute.standard, \ + javax.print.event, \ + javax.rmi, \ + javax.rmi.CORBA, \ + javax.rmi.ssl, \ + javax.script, \ + javax.security.auth, \ + javax.security.auth.callback, \ + javax.security.auth.kerberos, \ + javax.security.auth.login, \ + javax.security.auth.spi, \ + javax.security.auth.x500, \ + javax.security.cert, \ + javax.security.sasl, \ + javax.sound.midi, \ + javax.sound.midi.spi, \ + javax.sound.sampled, \ + javax.sound.sampled.spi, \ + javax.sql, \ + javax.sql.rowset, \ + javax.sql.rowset.serial, \ + javax.sql.rowset.spi, \ + javax.swing, \ + javax.swing.border, \ + javax.swing.colorchooser, \ + javax.swing.event, \ + javax.swing.filechooser, \ + javax.swing.plaf, \ + javax.swing.plaf.basic, \ + javax.swing.plaf.metal, \ + javax.swing.plaf.multi, \ + javax.swing.plaf.synth, \ + javax.swing.table, \ + javax.swing.text, \ + javax.swing.text.html, \ + javax.swing.text.html.parser, \ + javax.swing.text.rtf, \ + javax.swing.tree, \ + javax.swing.undo, \ + javax.tools, \ + javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \ + javax.xml, \ + javax.xml.bind;version="2.2.1", \ + javax.xml.bind.annotation;version="2.2.1", \ + javax.xml.bind.annotation.adapters;version="2.2.1", \ + javax.xml.bind.attachment;version="2.2.1", \ + javax.xml.bind.helpers;version="2.2.1", \ + javax.xml.bind.util;version="2.2.1", \ + javax.xml.crypto, \ + javax.xml.crypto.dom, \ + javax.xml.crypto.dsig, \ + javax.xml.crypto.dsig.dom, \ + javax.xml.crypto.dsig.keyinfo, \ + javax.xml.crypto.dsig.spec, \ + javax.xml.datatype, \ + javax.xml.namespace, \ + javax.xml.parsers, \ + javax.xml.soap;version="1.3", \ + javax.xml.stream;version="1.2", \ + javax.xml.stream.events;version="1.2", \ + javax.xml.stream.util;version="1.2", \ + javax.xml.transform, \ + javax.xml.transform.dom, \ + javax.xml.transform.sax, \ + javax.xml.transform.stax, \ + javax.xml.transform.stream, \ + javax.xml.validation, \ + javax.xml.ws;version="2.2", \ + javax.xml.ws.handler;version="2.2", \ + javax.xml.ws.handler.soap;version="2.2", \ + javax.xml.ws.http;version="2.2", \ + javax.xml.ws.soap;version="2.2", \ + javax.xml.ws.spi;version="2.2", \ + javax.xml.ws.wsaddressing;version="2.2", \ + javax.xml.ws.spi.http;version="2.2", \ + javax.xml.xpath, \ + org.ietf.jgss, \ + org.omg.CORBA, \ + org.omg.CORBA_2_3, \ + org.omg.CORBA_2_3.portable, \ + org.omg.CORBA.DynAnyPackage, \ + org.omg.CORBA.ORBPackage, \ + org.omg.CORBA.portable, \ + org.omg.CORBA.TypeCodePackage, \ + org.omg.CosNaming, \ + org.omg.CosNaming.NamingContextExtPackage, \ + org.omg.CosNaming.NamingContextPackage, \ + org.omg.Dynamic, \ + org.omg.DynamicAny, \ + org.omg.DynamicAny.DynAnyFactoryPackage, \ + org.omg.DynamicAny.DynAnyPackage, \ + org.omg.IOP, \ + org.omg.IOP.CodecFactoryPackage, \ + org.omg.IOP.CodecPackage, \ + org.omg.Messaging, \ + org.omg.PortableInterceptor, \ + org.omg.PortableInterceptor.ORBInitInfoPackage, \ + org.omg.PortableServer, \ + org.omg.PortableServer.CurrentPackage, \ + org.omg.PortableServer.POAManagerPackage, \ + org.omg.PortableServer.POAPackage, \ + org.omg.PortableServer.portable, \ + org.omg.PortableServer.ServantLocatorPackage, \ + org.omg.SendingContext, \ + org.omg.stub.java.rmi, \ + org.omg.stub.javax.management.remote.rmi, \ + org.w3c.dom, \ + org.w3c.dom.bootstrap, \ + org.w3c.dom.css, \ + org.w3c.dom.events, \ + org.w3c.dom.html, \ + org.w3c.dom.ls, \ + org.w3c.dom.ranges, \ + org.w3c.dom.stylesheets, \ + org.w3c.dom.traversal, \ + org.w3c.dom.views, \ + org.w3c.dom.xpath, \ + org.xml.sax, \ + org.xml.sax.ext, \ + org.xml.sax.helpers diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties new file mode 100644 index 0000000000..ca8c83c380 --- /dev/null +++ b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties @@ -0,0 +1,53 @@ +#Bundles to be started on startup, with startlevel + +# feature: framework version: 3.0.1 +mvn\:org.ops4j.base/ops4j-base-lang/1.4.0 = 5 +mvn\:biz.aQute.bnd/bndlib/2.2.0 = 5 +mvn\:org.ops4j.pax.swissbox/pax-swissbox-bnd/1.7.0 = 5 +mvn\:org.ops4j.pax.url/pax-url-maven-commons/1.6.0 = 5 +mvn\:org.ops4j.pax.url/pax-url-aether/1.6.0 = 5 +mvn\:org.ops4j.pax.url/pax-url-wrap/1.6.0 = 5 +mvn\:javax.annotation/javax.annotation-api/1.2 = 5 +mvn\:org.ops4j.pax.logging/pax-logging-api/1.7.2 = 8 +mvn\:org.ops4j.pax.logging/pax-logging-service/1.7.2 = 8 +mvn\:org.apache.karaf.service/org.apache.karaf.service.guard/3.0.1 = 10 +mvn\:org.apache.felix/org.apache.felix.configadmin/1.6.0 = 10 +mvn\:org.apache.felix/org.apache.felix.fileinstall/3.2.8 = 11 +mvn\:org.ow2.asm/asm-all/4.1 = 12 +mvn\:org.apache.aries/org.apache.aries.util/1.1.0 = 20 +mvn\:org.apache.aries.proxy/org.apache.aries.proxy.api/1.0.0 = 20 +mvn\:org.apache.aries.proxy/org.apache.aries.proxy.impl/1.0.2 = 20 +mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.api/1.0.0 = 20 +mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.cm/1.0.3 = 20 +mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core.compatibility/1.0.0 = 20 +mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core/1.4.0 = 20 +mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/3.0.1 = 24 +mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.blueprint/3.0.1 = 24 +mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.wrap/3.0.1 = 24 +mvn\:org.apache.karaf.region/org.apache.karaf.region.core/3.0.1 = 25 +mvn\:org.apache.karaf.features/org.apache.karaf.features.core/3.0.1 = 25 +mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.features/3.0.1 = 26 +mvn\:jline/jline/2.11 = 30 +mvn\:org.jledit/core/0.2.1 = 30 +mvn\:org.fusesource.jansi/jansi/1.11 = 30 +mvn\:org.ops4j.base/ops4j-base-util-property/1.4.0 = 30 +mvn\:org.ops4j.base/ops4j-base-util-xml/1.4.0 = 30 +mvn\:org.ops4j.base/ops4j-base-util-collections/1.4.0 = 30 +mvn\:org.ops4j.pax.url/pax-url-commons/1.6.0 = 30 +mvn\:org.ops4j.pax.swissbox/pax-swissbox-property/1.7.0 = 30 +mvn\:org.ops4j.base/ops4j-base-net/1.4.0 = 30 +mvn\:org.ops4j.base/ops4j-base-monitors/1.4.0 = 30 +mvn\:org.apache.karaf.features/org.apache.karaf.features.command/3.0.1 = 30 +mvn\:org.apache.karaf.shell/org.apache.karaf.shell.console/3.0.1 = 30 +mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/3.0.1 = 30 +mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.config/3.0.1 = 30 +mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.boot/3.0.1 = 30 +mvn\:org.apache.sshd/sshd-core/0.9.0 = 30 +mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.command/3.0.1 = 30 +mvn\:org.apache.karaf.shell/org.apache.karaf.shell.table/3.0.1 = 30 +mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.core/3.0.1 = 30 +mvn\:org.apache.karaf.shell/org.apache.karaf.shell.help/3.0.1 = 30 +mvn\:org.apache.karaf.system/org.apache.karaf.system.core/3.0.1 = 30 +mvn\:org.apache.karaf.system/org.apache.karaf.system.command/3.0.1 = 30 +mvn\:org.apache.karaf.shell/org.apache.karaf.shell.commands/3.0.1 = 30 +mvn\:org.apache.aries.quiesce/org.apache.aries.quiesce.api/1.0.0 = 30 diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 5b44bb7569..3802370aca 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -902,6 +902,10 @@ org.opendaylight.controller netconf-ssh
+ + org.opendaylight.controller + netconf-tcp + org.opendaylight.controller netconf-util diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index f15f8f7404..f05afbb346 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -14,13 +14,11 @@ osgi.bundles=\ # Netconf startup configuration -# Netconf tcp address:port is optional with default value 127.0.0.1:8383 +# Netconf tcp address:port is optional #netconf.tcp.address=127.0.0.1 -#netconf.tcp.port=8384 - -#netconf.tcp.client.address=127.0.0.1 -#netconf.tcp.client.port=8384 +#netconf.tcp.port=8383 +# Netconf tcp address:port is optional netconf.ssh.address=0.0.0.0 netconf.ssh.port=1830 netconf.ssh.pk.path = ./configuration/RSA.pk diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml index fcbec19f5f..b4b433dbca 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml @@ -28,6 +28,24 @@ prefix:netty-timer global-timer + + + + + prefix:threadfactory-naming + global-netconf-processing-executor-threadfactory + remote-connector-processing-executor + + + prefix:threadpool-flexible + global-netconf-processing-executor + 1 + 4 + 600000 + + prefix:threadfactory + global-netconf-processing-executor-threadfactory + @@ -43,6 +61,10 @@ prefix:netty-event-executor global-event-executor + + prefix:binding-broker-osgi-registry + binding-osgi-broker + prefix:dom-broker-osgi-registry dom-broker @@ -51,6 +73,10 @@ prefix:netconf-client-dispatcher global-netconf-dispatcher + + prefix:threadpool + global-netconf-processing-executor + @@ -62,6 +88,20 @@ /modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher'] + + prefix:threadfactory + + global-netconf-processing-executor-threadfactory + /modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory'] + + + + prefix:threadpool + + global-netconf-processing-executor + /modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor'] + + @@ -69,5 +109,7 @@ urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&revision=2013-10-28 urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&revision=2014-04-08 + urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05 + urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&revision=2013-12-01 diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java new file mode 100644 index 0000000000..b37b50159f --- /dev/null +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.inventory; + +import java.util.ArrayList; +import java.util.Set; + +import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider; +import org.opendaylight.controller.sal.compatibility.InventoryMapping; +import org.opendaylight.controller.switchmanager.ISwitchManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InventoryReader implements RuntimeDataProvider { + private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class); + private ISwitchManager switchManager; + + public ISwitchManager getSwitchManager() { + return switchManager; + } + + public void setSwitchManager(final ISwitchManager switchManager) { + this.switchManager = switchManager; + } + + @Override + public DataObject readConfigurationData(final InstanceIdentifier path) { + // Topology and Inventory are operational only + return null; + } + + @SuppressWarnings("unchecked") + @Override + public DataObject readOperationalData(final InstanceIdentifier path) { + final Class type = path.getTargetType(); + if (Nodes.class.equals(type)) { + return readNodes(((InstanceIdentifier) path)); + } + if (Node.class.equals(type)) { + return readNode(((InstanceIdentifier) path)); + } + if (NodeConnector.class.equals(type)) { + return readNodeConnector(((InstanceIdentifier) path)); + } + + LOG.debug("Unsupported type {}", type); + return null; + } + + private NodeConnector readNodeConnector(final InstanceIdentifier identifier) { + return constructNodeConnector(InventoryMapping.toAdNodeConnector(identifier)); + } + + private Node readNode(final InstanceIdentifier identifier) { + return constructNode(InventoryMapping.toAdNode(identifier)); + } + + private Node constructNode(final org.opendaylight.controller.sal.core.Node node) { + final Set connectors = getSwitchManager().getNodeConnectors(node); + final ArrayList tpList = new ArrayList(connectors.size()); + for (final org.opendaylight.controller.sal.core.NodeConnector connector : connectors) { + tpList.add(constructNodeConnector(connector)); + } + + return new NodeBuilder() + .setKey(InventoryMapping.toNodeKey(node)) + .setNodeConnector(tpList) + .build(); + } + + private Nodes readNodes(final InstanceIdentifier identifier) { + final Set nodes = getSwitchManager().getNodes(); + final ArrayList nodeList = new ArrayList(nodes.size()); + for (final org.opendaylight.controller.sal.core.Node node : nodes) { + nodeList.add(constructNode(node)); + } + + return new NodesBuilder().setNode(nodeList).build(); + } + + private static NodeConnector constructNodeConnector(final org.opendaylight.controller.sal.core.NodeConnector connector) { + return new NodeConnectorBuilder().setKey(InventoryMapping.toNodeConnectorKey(connector)).build(); + } +} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend deleted file mode 100644 index 9b71369593..0000000000 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.inventory - -import org.opendaylight.controller.switchmanager.ISwitchManager -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import org.opendaylight.yangtools.yang.binding.DataObject -import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider -import java.util.ArrayList -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector -import static extension org.opendaylight.controller.sal.compatibility.InventoryMapping.*; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder - -class InventoryReader implements RuntimeDataProvider { - - @Property - var ISwitchManager switchManager; - - override readConfigurationData(InstanceIdentifier path) { - - // Topology and Inventory are operational only - return null; - } - - override readOperationalData(InstanceIdentifier path) { - val type = path.targetType; - var DataObject data = null; - switch (type) { - case Nodes: - data = readNodes(path as InstanceIdentifier) - case Node: - data = readNode(path as InstanceIdentifier) - case NodeConnector: - data = readNodeConnector(path as InstanceIdentifier) - } - return data; - } - - def DataObject readNodeConnector(InstanceIdentifier identifier) { - val nodeConnector = identifier.toAdNodeConnector(); - return constructNodeConnector(nodeConnector) - } - - def DataObject readNode(InstanceIdentifier identifier) { - val node = identifier.toAdNode(); - return constructNode(node); - } - - - def Node constructNode(org.opendaylight.controller.sal.core.Node node) { - val connectors = switchManager.getNodeConnectors(node) - - val tpList = new ArrayList(connectors.size) - for (connector : connectors) { - tpList.add(constructNodeConnector(connector)); - } - - val it = new NodeBuilder() - key = node.toNodeKey(); - nodeConnector = tpList - return build(); - } - - def NodeConnector constructNodeConnector(org.opendaylight.controller.sal.core.NodeConnector connector) { - val it = new NodeConnectorBuilder() - key = connector.toNodeConnectorKey() - return build(); - } - - def readNodes(InstanceIdentifier identifier) { - val nodes = switchManager.nodes - val nodeList = new ArrayList(nodes.size) - for (node : nodes) { - nodeList.add(constructNode(node)) - } - val it = new NodesBuilder(); - node = nodeList - return build() - - } -} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java new file mode 100644 index 0000000000..8e50bd881e --- /dev/null +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topology; + +import java.util.Iterator; + +import org.opendaylight.controller.sal.compatibility.InventoryMapping; +import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Edge; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +import com.google.common.base.Splitter; + +public class TopologyMapping { + private static final String HEAD_TAIL_STRING = "::::"; + private static final Splitter HEAD_TAIL_SPLITTER = Splitter.on(HEAD_TAIL_STRING); + + public TopologyMapping(final TopologyKey path, final InstanceIdentifier key) { + // No-op for now. Multi-instance will require fixing InventoryMapping first. + } + + public Edge toAdTopologyEdge(final InstanceIdentifier identifier) throws ConstructionException { + @SuppressWarnings("unchecked") + final LinkKey linkKey = ((KeyedInstanceIdentifier)identifier).getKey(); + + final Iterator it = HEAD_TAIL_SPLITTER.split(linkKey.getLinkId().getValue()).iterator(); + final NodeConnector tail = InventoryMapping.nodeConnectorFromId(it.next()); + final NodeConnector head = InventoryMapping.nodeConnectorFromId(it.next()); + return new Edge(tail, head); + } + + public NodeConnector toAdTopologyNodeConnector(final InstanceIdentifier identifier) { + @SuppressWarnings("unchecked") + final TerminationPointKey tpKey = ((KeyedInstanceIdentifier)identifier).getKey(); + + return InventoryMapping.nodeConnectorFromId(tpKey.getTpId().getValue()); + } + + public org.opendaylight.controller.sal.core.Node toAdTopologyNode(final InstanceIdentifier identifier) { + @SuppressWarnings("unchecked") + final NodeKey nodeKey = ((KeyedInstanceIdentifier)identifier).getKey(); + + return InventoryMapping.nodeFromNodeId(nodeKey.getNodeId().getValue()); + } + + public NodeKey toTopologyNodeKey(final org.opendaylight.controller.sal.core.Node node) { + return new NodeKey(new NodeId(InventoryMapping.toNodeId(node))); + } + + public TerminationPointKey toTopologyTerminationPointKey(final NodeConnector nc) { + return new TerminationPointKey(new TpId(InventoryMapping.toNodeConnectorId(nc))); + } + + public LinkKey toTopologyLinkKey(final Edge edge) { + final TerminationPointKey sourceTp = toTopologyTerminationPointKey(edge.getTailNodeConnector()); + final TerminationPointKey destTp = toTopologyTerminationPointKey(edge.getHeadNodeConnector()); + + final StringBuilder sb = new StringBuilder(); + sb.append(sourceTp.getTpId().toString()); + sb.append(HEAD_TAIL_STRING); + sb.append(destTp.getTpId().toString()); + return new LinkKey(new LinkId(sb.toString())); + } +} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend deleted file mode 100644 index 0889de17b6..0000000000 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topology - -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import org.opendaylight.yangtools.yang.binding.DataObject -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology -import org.opendaylight.controller.sal.core.Edge -import java.util.Set -import org.opendaylight.controller.sal.core.Property -import org.opendaylight.controller.sal.core.NodeConnector - -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node -import org.opendaylight.controller.sal.compatibility.InventoryMapping -class TopologyMapping { - - new(TopologyKey path, InstanceIdentifier key) { - // NOOP - } - - def Edge toAdTopologyEdge(InstanceIdentifier identifier) { - val linkKey = (identifier.path.last as IdentifiableItem).key; - val components = linkKey.linkId.value.split("::::"); - val tail = InventoryMapping.nodeConnectorFromId(components.get(0)); - val head = InventoryMapping.nodeConnectorFromId(components.get(1)); - return new Edge(tail, head); - } - - def NodeConnector toAdTopologyNodeConnector(InstanceIdentifier identifier) { - val tpKey = (identifier.path.last as IdentifiableItem).key; - return InventoryMapping.nodeConnectorFromId(tpKey.tpId.value); - } - - def org.opendaylight.controller.sal.core.Node toAdTopologyNode( - InstanceIdentifier identifier) { - val tpKey = (identifier.path.last as IdentifiableItem).key; - return InventoryMapping.nodeFromNodeId(tpKey.nodeId.value); - } - - - - def NodeKey toTopologyNodeKey(org.opendaylight.controller.sal.core.Node node) { - val nodeId = new NodeId(InventoryMapping.toNodeId(node)); - return new NodeKey(nodeId); - } - - def TerminationPointKey toTopologyTerminationPointKey(NodeConnector nc) { - val node = nc.node; - val nodeId = new TpId(InventoryMapping.toNodeConnectorId(nc)) - return new TerminationPointKey(nodeId); - } - - def LinkKey toTopologyLinkKey(Edge edge) { - val sourceTp = edge.tailNodeConnector.toTopologyTerminationPointKey; - val destTp = edge.headNodeConnector.toTopologyTerminationPointKey; - val linkId = new LinkId('''«sourceTp.tpId»::::«destTp.tpId»''') - return new LinkKey(linkId); - } -} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java new file mode 100644 index 0000000000..a4ac6f94ee --- /dev/null +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topology; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider; +import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Edge; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.Property; +import org.opendaylight.controller.switchmanager.ISwitchManager; +import org.opendaylight.controller.topologymanager.ITopologyManager; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TopologyReader implements RuntimeDataProvider { + private static final Logger LOG = LoggerFactory.getLogger(TopologyReader.class); + private final InstanceIdentifier topologyPath; + private final TopologyKey topologyKey; + private final TopologyMapping mapping; + private ITopologyManager topologyManager; + private ISwitchManager switchManager; + + public ISwitchManager getSwitchManager() { + return this.switchManager; + } + + public void setSwitchManager(final ISwitchManager switchManager) { + this.switchManager = switchManager; + } + + public ITopologyManager getTopologyManager() { + return this.topologyManager; + } + + public void setTopologyManager(final ITopologyManager topologyManager) { + this.topologyManager = topologyManager; + } + + public TopologyKey getTopologyKey() { + return this.topologyKey; + } + + public TopologyMapping getMapping() { + return this.mapping; + } + + public TopologyReader() { + this.topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal")); + this.topologyPath = InstanceIdentifier.builder(NetworkTopology.class) + .child(Topology.class, topologyKey) + .toInstance(); + this.mapping = new TopologyMapping(topologyKey, topologyPath); + } + + @Override + public DataObject readConfigurationData(final InstanceIdentifier path) { + // Topology and Inventory are operational only + return null; + } + + @SuppressWarnings("unchecked") + @Override + public DataObject readOperationalData(final InstanceIdentifier path) { + if (!topologyPath.contains(path)) { + return null; + } + + final Class type = path.getTargetType(); + if (Link.class.equals(type)) { + return readLink((InstanceIdentifier) path); + } + if (Node.class.equals(type)) { + return readNode((InstanceIdentifier) path); + } + if (TerminationPoint.class.equals(type)) { + return readTerminationPoint((InstanceIdentifier) path); + + } + if (Topology.class.equals(type)) { + return readTopology((InstanceIdentifier) path); + } + + LOG.debug("Unsupported type {}", type); + return null; + } + + private Link readLink(final InstanceIdentifier identifier) { + final Edge edge; + try { + edge = this.mapping.toAdTopologyEdge(identifier); + } catch (ConstructionException e) { + throw new IllegalStateException(String.format("Failed to construct edge for link %s", identifier), e); + } + + final Map> edges; + if (topologyManager != null) { + edges = topologyManager.getEdges(); + } else { + edges = null; + } + + final Set properties; + if (edges != null) { + properties = edges.get(edge); + } else { + properties = null; + } + + return constructLink(edge); + } + + private TerminationPoint readTerminationPoint(final InstanceIdentifier identifier) { + return constructTerminationPoint(mapping.toAdTopologyNodeConnector(identifier)); + } + + private Node readNode(final InstanceIdentifier identifier) { + return constructNode(mapping.toAdTopologyNode(identifier)); + } + + private Topology readTopology(final InstanceIdentifier identifier) { + final Set nodes = getSwitchManager().getNodes(); + final ArrayList nodeList = new ArrayList(nodes.size()); + for (final org.opendaylight.controller.sal.core.Node node : nodes) { + nodeList.add(constructNode(node)); + } + + final Map> edges = getTopologyManager().getEdges(); + final ArrayList linkList = new ArrayList(edges.size()); + for (final Edge edge : edges.keySet()) { + linkList.add(constructLink(edge)); + } + + return new TopologyBuilder() + .setKey(topologyKey) + .setNode(nodeList) + .setLink(linkList) + .build(); + } + + private Link constructLink(final Edge edge) { + final NodeConnector sourceNc = edge.getTailNodeConnector(); + final NodeConnector destNc = edge.getHeadNodeConnector(); + + final LinkBuilder it = new LinkBuilder().setKey(mapping.toTopologyLinkKey(edge)); + + it.setSource(new SourceBuilder() + .setSourceNode(mapping.toTopologyNodeKey(sourceNc.getNode()).getNodeId()) + .setSourceTp(mapping.toTopologyTerminationPointKey(sourceNc).getTpId()) + .build()); + + it.setDestination(new DestinationBuilder() + .setDestNode(mapping.toTopologyNodeKey(destNc.getNode()).getNodeId()) + .setDestTp(mapping.toTopologyTerminationPointKey(destNc).getTpId()) + .build()); + + return it.build(); + } + + private Node constructNode(final org.opendaylight.controller.sal.core.Node node) { + final Set connectors = getSwitchManager().getNodeConnectors(node); + final ArrayList tpList = new ArrayList(connectors.size()); + for (final NodeConnector connector : connectors) { + tpList.add(constructTerminationPoint(connector)); + } + + return new NodeBuilder() + .setKey(mapping.toTopologyNodeKey(node)) + .setTerminationPoint(tpList) + .build(); + } + + private TerminationPoint constructTerminationPoint(final NodeConnector connector) { + return new TerminationPointBuilder().setKey(mapping.toTopologyTerminationPointKey(connector)).build(); + } +} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend deleted file mode 100644 index 6ebe20b84a..0000000000 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topology - -import java.util.ArrayList -import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider -import org.opendaylight.controller.sal.core.Edge -import org.opendaylight.controller.sal.core.NodeConnector -import org.opendaylight.controller.switchmanager.ISwitchManager -import org.opendaylight.controller.topologymanager.ITopologyManager -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder -import org.opendaylight.yangtools.yang.binding.DataObject -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier - -class TopologyReader implements RuntimeDataProvider { - - @Property - var ISwitchManager switchManager; - - @Property - var ITopologyManager topologyManager; - - @Property - val TopologyKey topologyKey; - - @Property - val InstanceIdentifier topologyPath; - - @Property - val extension TopologyMapping mapping; - - new() { - _topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal")); - _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topologyKey).toInstance; - _mapping = new TopologyMapping(topologyKey, topologyPath); - } - - override readConfigurationData(InstanceIdentifier path) { - - // Topology and Inventory are operational only - return null; - } - - override readOperationalData(InstanceIdentifier path) { - val type = path.targetType; - var DataObject data = null; - if (false == topologyPath.contains(path)) { - return null; - } - switch (type) { - case Topology: - data = readTopology(path as InstanceIdentifier) - case Node: - data = readNode(path as InstanceIdentifier) - case TerminationPoint: - data = readTerminationPoint(path as InstanceIdentifier) - case Link: - data = readLink(path as InstanceIdentifier) - } - return data; - } - - def DataObject readLink(InstanceIdentifier identifier) { - val edge = identifier.toAdTopologyEdge(); - val properties = topologyManager?.edges?.get(edge); - - return constructLink(edge); - } - - def DataObject readTerminationPoint(InstanceIdentifier identifier) { - val nodeConnector = identifier.toAdTopologyNodeConnector(); - return constructTerminationPoint(nodeConnector) - } - - def DataObject readNode(InstanceIdentifier identifier) { - val node = identifier.toAdTopologyNode(); - return constructNode(node); - } - - def DataObject readTopology(InstanceIdentifier identifier) { - - //val nodeConnectors = switchManager. - val nodes = switchManager.nodes - val edges = topologyManager.edges - - val nodeList = new ArrayList(nodes.size) - for (node : nodes) { - nodeList.add(constructNode(node)) - } - - val linkList = new ArrayList(edges.size) - for (edge : edges.keySet) { - linkList.add(constructLink(edge)) - } - - val it = new TopologyBuilder(); - key = topologyKey - node = nodeList - link = linkList - return build() - } - - def constructLink(Edge edge) { - val sourceNc = edge.tailNodeConnector - val destNc = edge.headNodeConnector - - val it = new LinkBuilder() - key = edge.toTopologyLinkKey(); - source = new SourceBuilder().setSourceNode(sourceNc.node.toTopologyNodeKey.nodeId).setSourceTp( - sourceNc.toTopologyTerminationPointKey.tpId).build() - destination = new DestinationBuilder().setDestNode(destNc.node.toTopologyNodeKey.nodeId).setDestTp( - destNc.toTopologyTerminationPointKey.tpId).build - return build() - } - - def Node constructNode(org.opendaylight.controller.sal.core.Node node) { - val connectors = switchManager.getNodeConnectors(node) - - val tpList = new ArrayList(connectors.size) - for (connector : connectors) { - tpList.add(constructTerminationPoint(connector)); - } - - val it = new NodeBuilder() - key = node.toTopologyNodeKey(); - terminationPoint = tpList - return build(); - } - - def TerminationPoint constructTerminationPoint(NodeConnector connector) { - val it = new TerminationPointBuilder() - key = connector.toTopologyTerminationPointKey - return build(); - } - -} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java new file mode 100644 index 0000000000..a7a7a9ac3a --- /dev/null +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topologymanager; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.opendaylight.controller.sal.compatibility.NodeMapping; +import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Edge; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.Property; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class AdSalTopologyMapping { + private final InstanceIdentifier topologyPath; + + public AdSalTopologyMapping(final TopologyKey topology) { + this.topologyPath = InstanceIdentifier.builder(NetworkTopology.class) + .child(Topology.class, topology).toInstance(); + } + + public InstanceIdentifier getTopologyPath() { + return topologyPath; + } + + public InstanceIdentifier toTerminationPoint(final NodeConnector connector) { + return getTopologyPath().builder() + .child(Node.class) + .child(TerminationPoint.class, toTerminationPointKey(connector)) + .toInstance(); + } + + public Map> toEdgePropertiesMap(final Iterable links) { + final HashMap> ret = new HashMap<>(); + for (final Link link : links) { + try { + ret.put(toEdge(link), toProperties(link)); + } catch (ConstructionException e) { + throw new IllegalStateException(String.format("Failed to create edge properties for {}", link), e); + } + } + return ret; + } + + public static Set toEdges(final Iterable links) throws ConstructionException { + final HashSet ret = new HashSet(); + for (final Link link : links) { + ret.add(toEdge(link)); + } + return ret; + } + + public static Edge toEdge(final Link link) throws ConstructionException { + final NodeConnector tail = toNodeConnector(link.getSource()); + final NodeConnector head = toNodeConnector(link.getDestination()); + return new Edge(tail, head); + } + + public static org.opendaylight.controller.sal.core.Node toAdNode(final Node node) throws ConstructionException { + return toAdNode(node.getNodeId()); + } + + public static org.opendaylight.controller.sal.core.Node toAdNode(final NodeId node) throws ConstructionException { + final NodeKey key = new NodeKey( + new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId(node)); + return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, key); + } + + public static NodeConnector toNodeConnector(final Source ref) throws ConstructionException { + final org.opendaylight.controller.sal.core.Node adNode = toAdNode(ref.getSourceNode()); + final NodeConnectorKey key = new NodeConnectorKey(new NodeConnectorId(ref.getSourceTp())); + return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode); + } + + public static NodeConnector toNodeConnector(final Destination ref) throws ConstructionException { + final org.opendaylight.controller.sal.core.Node adNode = toAdNode(ref.getDestNode()); + final NodeConnectorKey key = new NodeConnectorKey(new NodeConnectorId(ref.getDestTp())); + return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode); + } + + public TerminationPointKey toTerminationPointKey(final NodeConnector connector) { + return null; + } + + public Set toProperties(final Link link) { + return null; + } +} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend deleted file mode 100644 index aa238a8a8e..0000000000 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topologymanager - -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint -import org.opendaylight.controller.sal.core.NodeConnector -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology -import java.util.Map -import org.opendaylight.controller.sal.core.Edge -import java.util.Set -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node -import java.util.HashSet -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId -import org.opendaylight.controller.sal.compatibility.NodeMapping -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey -import java.util.HashMap - -class AdSalTopologyMapping { - - val TopologyKey topologyMapping; - @Property - val InstanceIdentifier topologyPath; - - new(TopologyKey topology) { - topologyMapping = topology; - _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topology).toInstance; - } - - def InstanceIdentifier toTerminationPoint(NodeConnector connector) { - InstanceIdentifier.builder(topologyPath).child(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance; - } - - def Map> toEdgePropertiesMap(Iterable links) { - val ret = new HashMap> - for (link : links) { - ret.put(link.toEdge(), link.toProperties()) - } - return ret; - } - - def Set toEdges(Iterable links) { - val ret = new HashSet - for (link : links) { - ret.add(link.toEdge) - } - return ret; - } - - def Edge toEdge(Link link) { - val tail = link.source.toNodeConnector(); - val head = link.destination.toNodeConnector(); - return new Edge(tail, head); - } - - def org.opendaylight.controller.sal.core.Node toAdNode(Node node) { - return node.nodeId.toAdNode; - } - - def org.opendaylight.controller.sal.core.Node toAdNode( - org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId node) { - val key = new NodeKey(new NodeId(node)) - return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, key); - } - - def NodeConnector toNodeConnector(Source ref) { - val adNode = ref.sourceNode.toAdNode(); - val key = new NodeConnectorKey(new NodeConnectorId(ref.sourceTp)) - return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode); - } - - def NodeConnector toNodeConnector(Destination ref) { - val adNode = ref.destNode.toAdNode(); - val key = new NodeConnectorKey(new NodeConnectorId(ref.destTp)) - return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode); - } - - def TerminationPointKey toTerminationPointKey(NodeConnector connector) { - } - - def Set toProperties(Link link) { - } -} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java new file mode 100644 index 0000000000..11320a12cd --- /dev/null +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topologymanager; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader; +import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Edge; +import org.opendaylight.controller.sal.core.Host; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.Property; +import org.opendaylight.controller.sal.core.UpdateType; +import org.opendaylight.controller.sal.utils.Status; +import org.opendaylight.controller.topologymanager.ITopologyManager; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; + +@SuppressWarnings("all") +public class CompatibleTopologyManager extends ConfigurableLinkManager implements ITopologyManager { + private AdSalTopologyMapping topologyMapping; + private TypeSafeDataReader dataReader; + + public TypeSafeDataReader getDataReader() { + return dataReader; + } + + public void setDataReader(final TypeSafeDataReader dataReader) { + this.dataReader = dataReader; + } + + public AdSalTopologyMapping getTopologyMapping() { + return topologyMapping; + } + + public void setTopologyMapping(final AdSalTopologyMapping topologyMapping) { + this.topologyMapping = topologyMapping; + } + + @Override + public Map> getEdges() { + final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath()); + return this.topologyMapping.toEdgePropertiesMap(topology.getLink()); + } + + @Override + public Map> getNodeEdges() { + final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath()); + final HashMap> ret = new HashMap<>(); + for (final Node node : topology.getNode()) { + try { + ret.put(topologyMapping.toAdNode(node), topologyMapping.toEdges( + FluentIterable.from(topology.getLink()).filter(new Predicate() { + @Override + public boolean apply(final Link input) { + final NodeId nodeId = node.getNodeId(); + if (nodeId.equals(input.getSource().getSourceNode())) { + return true; + } + if (nodeId.equals(input.getDestination().getDestNode())) { + return true; + } + + return false; + } + }))); + } catch (ConstructionException e) { + throw new IllegalStateException(String.format("Failed to construct node for {}", node), e); + } + } + return ret; + } + + /** + * Returns true if point is connected to link + */ + private boolean isInternal(final TerminationPoint point) { + final Topology topology = getDataReader().readConfigurationData(topologyMapping.getTopologyPath()); + final TpId tpId = point.getKey().getTpId(); + return FluentIterable.from(topology.getLink()).anyMatch(new Predicate() { + @Override + public boolean apply(final Link input) { + if (tpId.equals(input.getSource().getSourceTp())) { + return true; + } + if (tpId.equals(input.getDestination().getDestTp())) { + return true; + } + return false; + } + }); + } + + @Override + public Set getNodeConnectorWithHost() { + return null; + } + + @Override + public Host getHostAttachedToNodeConnector(final NodeConnector p) { + final InstanceIdentifier tpPath = topologyMapping.toTerminationPoint(p); + throw new UnsupportedOperationException("TODO: auto-generated method stub"); + } + + @Override + public List getHostsAttachedToNodeConnector(final NodeConnector p) { + final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath()); + throw new UnsupportedOperationException("Hosts not mapped yet"); + } + + @Override + public Map> getNodesWithNodeConnectorHost() { + throw new UnsupportedOperationException("TODO: auto-generated method stub"); + } + + @Override + public boolean isInternal(final NodeConnector p) { + final TerminationPoint connector = getDataReader() + .readConfigurationData(topologyMapping.toTerminationPoint(p)); + return this.isInternal(connector); + } + + @Override + public void updateHostLink(final NodeConnector p, final Host h, final UpdateType t, final Set props) { + // Update app defined topology + } + + @Override + public Status saveConfig() { + // FIXME: commit configuration + return null; + } +} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend deleted file mode 100644 index 4fdea4b4c2..0000000000 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topologymanager - -import org.opendaylight.controller.topologymanager.ITopologyManager -import org.opendaylight.controller.sal.core.NodeConnector -import org.opendaylight.controller.sal.core.Host -import org.opendaylight.controller.sal.core.UpdateType -import java.util.Set -import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader -import java.util.HashMap -import org.opendaylight.controller.sal.core.Edge -import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint -import com.google.common.collect.FluentIterable - -class CompatibleTopologyManager extends ConfigurableLinkManager implements ITopologyManager { - - @Property - var TypeSafeDataReader dataReader; - - @Property - var extension AdSalTopologyMapping topologyMapping; - - override getEdges() { - val topology = dataReader.readOperationalData(topologyPath); - return topology.link.toEdgePropertiesMap(); - } - - override getNodeEdges() { - val topology = dataReader.readOperationalData(topologyPath); - val ret = new HashMap>; - for (node : topology.node) { - val adNode = node.toAdNode(); - val adEdges = FluentIterable.from(topology.link).filter[ - source.sourceNode == node.nodeId || destination.destNode == node.nodeId].toEdges(); - ret.put(adNode, adEdges) - } - return ret; - } - - /** - * Returns true if point is connected to link - */ - def isInternal(TerminationPoint point) { - val topology = dataReader.readConfigurationData(topologyPath); - val tpId = point.key.tpId; - return FluentIterable.from(topology.link).anyMatch( - [ - source.sourceTp == tpId || destination.destTp == tpId - ]) - } - - override getNodeConnectorWithHost() { - } - - override getHostAttachedToNodeConnector(NodeConnector p) { - val tpPath = p.toTerminationPoint(); - throw new UnsupportedOperationException("TODO: auto-generated method stub") - } - - override getHostsAttachedToNodeConnector(NodeConnector p) { - val topology = dataReader.readOperationalData(topologyPath); - - throw new UnsupportedOperationException("Hosts not mapped yet") - } - - override getNodesWithNodeConnectorHost() { - throw new UnsupportedOperationException("TODO: auto-generated method stub") - - } - - override isInternal(NodeConnector p) { - val tpPath = p.toTerminationPoint(); - val connector = dataReader.readConfigurationData(tpPath); - return connector.isInternal(); - } - - override updateHostLink(NodeConnector p, Host h, UpdateType t, - Set props) { - // Update app defined topology - } - - override saveConfig() { - // FIXME: commit configuration - } - -} diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.java similarity index 57% rename from opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.xtend rename to opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.java index b5f6cecd34..2d85f76278 100644 --- a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.xtend +++ b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.java @@ -1,31 +1,31 @@ -/* +/** * Copyright (c) 2014 Cisco Systems, 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.controller.md.compatibility.topologymanager +package org.opendaylight.controller.md.compatibility.topologymanager; -import org.opendaylight.controller.topologymanager.ITopologyManager -import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig +import java.util.concurrent.ConcurrentMap; -abstract class ConfigurableLinkManager implements ITopologyManager { - - final override addUserLink(TopologyUserLinkConfig link) { - throw new UnsupportedOperationException("TODO: auto-generated method stub") - +import org.opendaylight.controller.sal.utils.Status; +import org.opendaylight.controller.topologymanager.ITopologyManager; +import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig; + +public abstract class ConfigurableLinkManager implements ITopologyManager { + @Override + public final Status addUserLink(final TopologyUserLinkConfig link) { + throw new UnsupportedOperationException("TODO: auto-generated method stub"); } - - - final override deleteUserLink(String linkName) { - throw new UnsupportedOperationException("TODO: auto-generated method stub") - + + @Override + public final Status deleteUserLink(final String linkName) { + throw new UnsupportedOperationException("TODO: auto-generated method stub"); } - - - final override getUserLinks() { - throw new UnsupportedOperationException("TODO: auto-generated method stub") - + + @Override + public final ConcurrentMap getUserLinks() { + throw new UnsupportedOperationException("TODO: auto-generated method stub"); } } diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/pom.xml b/opendaylight/md-sal/compatibility/sal-compatibility/pom.xml index a145a3b4e1..6c79e053e1 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/pom.xml +++ b/opendaylight/md-sal/compatibility/sal-compatibility/pom.xml @@ -42,10 +42,6 @@ - - org.eclipse.xtend - xtend-maven-plugin - org.jacoco jacoco-maven-plugin diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java new file mode 100644 index 0000000000..2830165425 --- /dev/null +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java @@ -0,0 +1,770 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.compatibility; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader; +import org.opendaylight.controller.sal.binding.api.data.DataBrokerService; +import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; +import org.opendaylight.controller.sal.binding.api.data.DataProviderService; +import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Edge; +import org.opendaylight.controller.sal.core.Node; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.NodeTable; +import org.opendaylight.controller.sal.core.Property; +import org.opendaylight.controller.sal.core.UpdateType; +import org.opendaylight.controller.sal.inventory.IPluginInInventoryService; +import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService; +import org.opendaylight.controller.sal.reader.FlowOnNode; +import org.opendaylight.controller.sal.reader.IPluginInReadService; +import org.opendaylight.controller.sal.reader.IPluginOutReadService; +import org.opendaylight.controller.sal.reader.NodeConnectorStatistics; +import org.opendaylight.controller.sal.reader.NodeDescription; +import org.opendaylight.controller.sal.reader.NodeTableStatistics; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.Link; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.Bytes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.Packets; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatistics; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllNodeConnectorsStatisticsInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetNodeConnectorStatisticsInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInInventoryService, OpendaylightInventoryListener, OpendaylightFlowStatisticsListener, OpendaylightFlowTableStatisticsListener, OpendaylightPortStatisticsListener { + private static final Logger LOG = LoggerFactory.getLogger(InventoryAndReadAdapter.class); + private static final short OPENFLOWV10_TABLE_ID = 0; + + private final InventoryNotificationProvider inventoryNotificationProvider = new InventoryNotificationProvider(); + private final Map> nodeToNodeConnectorsMap = new ConcurrentHashMap<>(); + private List inventoryPublisher = new CopyOnWriteArrayList<>(); + private List statisticsPublisher = new CopyOnWriteArrayList<>(); + + private OpendaylightFlowTableStatisticsService flowTableStatisticsService; + private OpendaylightPortStatisticsService nodeConnectorStatisticsService; + private OpendaylightFlowStatisticsService flowStatisticsService; + private FlowTopologyDiscoveryService topologyDiscovery; + private DataProviderService dataProviderService; + private DataBrokerService dataService; + + public DataBrokerService getDataService() { + return dataService; + } + + public void setDataService(final DataBrokerService dataService) { + this.dataService = dataService; + } + + public DataProviderService getDataProviderService() { + return dataProviderService; + } + + public void setDataProviderService(final DataProviderService dataProviderService) { + this.dataProviderService = dataProviderService; + } + + public OpendaylightFlowStatisticsService getFlowStatisticsService() { + return flowStatisticsService; + } + + public void setFlowStatisticsService(final OpendaylightFlowStatisticsService flowStatisticsService) { + this.flowStatisticsService = flowStatisticsService; + } + + public OpendaylightPortStatisticsService getNodeConnectorStatisticsService() { + return nodeConnectorStatisticsService; + } + + public void setNodeConnectorStatisticsService(final OpendaylightPortStatisticsService nodeConnectorStatisticsService) { + this.nodeConnectorStatisticsService = nodeConnectorStatisticsService; + } + + public OpendaylightFlowTableStatisticsService getFlowTableStatisticsService() { + return flowTableStatisticsService; + } + + public void setFlowTableStatisticsService(final OpendaylightFlowTableStatisticsService flowTableStatisticsService) { + this.flowTableStatisticsService = flowTableStatisticsService; + } + + public FlowTopologyDiscoveryService getTopologyDiscovery() { + return topologyDiscovery; + } + + public void setTopologyDiscovery(final FlowTopologyDiscoveryService topologyDiscovery) { + this.topologyDiscovery = topologyDiscovery; + } + + public List getStatisticsPublisher() { + return statisticsPublisher; + } + + public void setStatisticsPublisher(final List statisticsPublisher) { + this.statisticsPublisher = statisticsPublisher; + } + + public List getInventoryPublisher() { + return inventoryPublisher; + } + + public void setInventoryPublisher(final List inventoryPublisher) { + this.inventoryPublisher = inventoryPublisher; + } + + public void startAdapter() { + inventoryNotificationProvider.setDataProviderService(getDataProviderService()); + inventoryNotificationProvider.setInventoryPublisher(getInventoryPublisher()); + // inventoryNotificationProvider.start(); + } + + public boolean setInventoryPublisher(final IPluginOutInventoryService listener) { + return getInventoryPublisher().add(listener); + } + + public boolean unsetInventoryPublisher(final IPluginOutInventoryService listener) { + return getInventoryPublisher().remove(listener); + } + + public boolean setReadPublisher(final IPluginOutReadService listener) { + return getStatisticsPublisher().add(listener); + } + + public Boolean unsetReadPublisher(final IPluginOutReadService listener) { + if (listener != null) { + return getStatisticsPublisher().remove(listener); + } + return false; + } + + protected DataModificationTransaction startChange() { + return getDataProviderService().beginTransaction(); + } + + @Override + public long getTransmitRate(final NodeConnector connector) { + final FlowCapableNodeConnector nodeConnector = this.readOperFlowCapableNodeConnector(NodeMapping.toNodeConnectorRef(connector)); + return nodeConnector.getCurrentSpeed().longValue(); + } + + private FlowCapableNode readOperFlowCapableNode(final NodeRef ref) { + final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node = + (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node)getDataService().readOperationalData(ref.getValue()); + if (node == null) { + return null; + } + + return node.getAugmentation(FlowCapableNode.class); + } + + private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node readConfigNode(final Node node) { + final InstanceIdentifier nodeRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(node)) + .build(); + + return (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node) startChange().readConfigurationData(nodeRef); + } + + private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector readConfigNodeConnector(final NodeConnector connector) { + final InstanceIdentifier nodeConnectorRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(connector.getNode())) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector.class, InventoryMapping.toNodeConnectorKey(connector)) + .toInstance(); + + return((org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector) startChange().readConfigurationData(nodeConnectorRef)); + } + + /** + * Read a table of a node from configuration data store. + * + * @param node Node id + * @param id Table id + * @return Table contents, or null if not present + */ + private Table readConfigTable(final Node node, final short id) { + final InstanceIdentifier tableRef = InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(node)) + .augmentation(FlowCapableNode.class) + .child(Table.class, new TableKey(id)) + .build(); + + return (Table) startChange().readConfigurationData(tableRef); + } + + @Override + public List readAllFlow(final Node node, final boolean cached) { + final ArrayList output = new ArrayList<>(); + final Table table = readConfigTable(node, OPENFLOWV10_TABLE_ID); + if (table != null) { + final List flows = table.getFlow(); + LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size()); + + for (final Flow flow : flows) { + final FlowStatisticsData statsFromDataStore = flow.getAugmentation(FlowStatisticsData.class); + if (statsFromDataStore != null) { + final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flow, node)); + output.add(addFlowStats(it, statsFromDataStore.getFlowStatistics())); + } + } + } + + // TODO (main): Shall we send request to the switch? It will make async request to the switch. + // Once the plugin receives a response, it will let the adaptor know through onFlowStatisticsUpdate() + // If we assume that md-sal statistics manager will always be running, then it is not required + // But if not, then sending request will collect the latest data for adaptor at least. + getFlowStatisticsService().getAllFlowsStatisticsFromAllFlowTables( + new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder().setNode(NodeMapping.toNodeRef(node)).build()); + return output; + } + + @Override + public List readAllNodeConnector(final Node node, final boolean cached) { + final ArrayList ret = new ArrayList<>(); + + final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node dsNode = readConfigNode(node); + if (dsNode != null) { + for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector dsNodeConnector : dsNode.getNodeConnector()) { + final FlowCapableNodeConnectorStatistics stats = (dsNodeConnector.getAugmentation(FlowCapableNodeConnectorStatisticsData.class)); + if (stats != null) { + try { + ret.add(toNodeConnectorStatistics(stats.getFlowCapableNodeConnectorStatistics(), dsNode.getId(), dsNodeConnector.getId())); + } catch (ConstructionException e) { + LOG.warn("Failed to instantiate node connector statistics for node {} connector {}, ignoring it", + dsNode.getId(), dsNodeConnector.getId(), e); + } + } + } + } + + //TODO: Refer TODO (main) + getNodeConnectorStatisticsService().getAllNodeConnectorsStatistics( + new GetAllNodeConnectorsStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(node)).build()); + return ret; + } + + @Override + public List readAllNodeTable(final Node node, final boolean cached) { + final NodeRef nodeRef = NodeMapping.toNodeRef(node); + + final ArrayList ret = new ArrayList<>(); + final FlowCapableNode dsFlowCapableNode = this.readOperFlowCapableNode(nodeRef); + if (dsFlowCapableNode != null) { + for (final Table table : dsFlowCapableNode.getTable()) { + final FlowTableStatisticsData tableStats = table.getAugmentation(FlowTableStatisticsData.class); + if (tableStats != null) { + try { + ret.add(toNodeTableStatistics(tableStats.getFlowTableStatistics(), table.getId(), node)); + } catch (ConstructionException e) { + LOG.warn("Failed to instantiate table statistics for node {} table {}, ignoring it", node, table.getId(), e); + } + } + } + } + + //TODO: Refer TODO (main) + getFlowTableStatisticsService().getFlowTablesStatistics(new GetFlowTablesStatisticsInputBuilder().setNode(nodeRef).build()); + return ret; + } + + @Override + public NodeDescription readDescription(final Node node, final boolean cached) { + return this.toNodeDescription(NodeMapping.toNodeRef(node)); + } + + @Override + public FlowOnNode readFlow(final Node node, final org.opendaylight.controller.sal.flowprogrammer.Flow targetFlow, final boolean cached) { + FlowOnNode ret = null; + final Table table = readConfigTable(node, OPENFLOWV10_TABLE_ID); + if (table != null) { + final List flows = table.getFlow(); + InventoryAndReadAdapter.LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size()); + + for (final Flow mdsalFlow : flows) { + if(FromSalConversionsUtils.flowEquals(mdsalFlow, MDFlowMapping.toMDSalflow(targetFlow))) { + final FlowStatisticsData statsFromDataStore = mdsalFlow.getAugmentation(FlowStatisticsData.class); + if (statsFromDataStore != null) { + InventoryAndReadAdapter.LOG.debug("Found matching flow in the data store flow table "); + ret = addFlowStats(new FlowOnNode(targetFlow), statsFromDataStore.getFlowStatistics()); + + // FIXME: break; ? + } + } + } + } + + //TODO: Refer TODO (main) + final GetFlowStatisticsFromFlowTableInputBuilder input = new GetFlowStatisticsFromFlowTableInputBuilder().setNode(NodeMapping.toNodeRef(node)); + input.fieldsFrom(MDFlowMapping.toMDSalflow(targetFlow)); + getFlowStatisticsService().getFlowStatisticsFromFlowTable(input.build()); + return ret; + } + + @Override + public NodeConnectorStatistics readNodeConnector(final NodeConnector connector, final boolean cached) { + final NodeConnectorId ncId = InventoryMapping.toNodeConnectorKey(connector).getId(); + + NodeConnectorStatistics ret = null; + final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nodeConnectorFromDS = readConfigNodeConnector(connector); + if (nodeConnectorFromDS != null) { + final FlowCapableNodeConnectorStatistics stats = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData.class); + if (stats != null) { + try { + ret = toNodeConnectorStatistics(stats.getFlowCapableNodeConnectorStatistics(), + InventoryMapping.toNodeKey(connector.getNode()).getId(), ncId); + } catch (ConstructionException e) { + LOG.warn("Failed to instantiate node connector statistics for connector {}, ignoring it", + connector, e); + } + } + } + + getNodeConnectorStatisticsService().getNodeConnectorStatistics( + new GetNodeConnectorStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(connector.getNode())).setNodeConnectorId(ncId).build()); + return ret; + } + + @Override + public NodeTableStatistics readNodeTable(final NodeTable nodeTable, final boolean cached) { + NodeTableStatistics nodeStats = null; + final Table table = readConfigTable(nodeTable.getNode(), (short) nodeTable.getID()); + if (table != null) { + final FlowTableStatisticsData tableStats = table.getAugmentation(FlowTableStatisticsData.class); + if (tableStats != null) { + try { + nodeStats = toNodeTableStatistics(tableStats.getFlowTableStatistics(), table.getId(), nodeTable.getNode()); + } catch (ConstructionException e) { + LOG.warn("Failed to instantiate table statistics for node {} table {}, ignoring it", + nodeTable.getNode(), table.getId(), e); + } + } + } + + //TODO: Refer TODO (main) + getFlowTableStatisticsService().getFlowTablesStatistics( + new GetFlowTablesStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(nodeTable.getNode())).build()); + return nodeStats; + } + + @Override + public void onNodeConnectorRemoved(final NodeConnectorRemoved update) { + // Never received + } + + @Override + public void onNodeRemoved(final NodeRemoved notification) { + this.removeNodeConnectors(notification.getNodeRef().getValue()); + try { + final Node aDNode = NodeMapping.toADNode(notification.getNodeRef()); + this.publishNodeUpdate(aDNode, UpdateType.REMOVED, Collections.emptySet()); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, not propagating update", notification.getNodeRef(), e); + } + } + + @Override + public void onNodeConnectorUpdated(final NodeConnectorUpdated update) { + final NodeConnectorRef ref = update.getNodeConnectorRef(); + final UpdateType updateType; + if (!this.isKnownNodeConnector(ref.getValue())) { + this.recordNodeConnector(ref.getValue()); + updateType = UpdateType.ADDED; + } else { + updateType = UpdateType.CHANGED; + } + + try { + final NodeConnector nodeConnector; + nodeConnector = NodeMapping.toADNodeConnector(ref); + this.publishNodeConnectorUpdate(nodeConnector, updateType, NodeMapping.toADNodeConnectorProperties(update)); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node connector for {}, not reporting the update", ref, e); + } + } + + @Override + public void onNodeUpdated(final NodeUpdated notification) { + final NodeRef ref = notification.getNodeRef(); + + final UpdateType updateType; + if (dataService.readOperationalData(ref.getValue()) == null) { + updateType = UpdateType.ADDED; + } else { + updateType = UpdateType.CHANGED; + } + + final Node aDNode; + try { + aDNode = NodeMapping.toADNode(ref); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, not reporting the update", ref, e); + return; + } + + this.publishNodeUpdate(aDNode, updateType, NodeMapping.toADNodeProperties(notification)); + for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) { + final NodeDescription description = this.toNodeDescription(ref); + if (description != null) { + final InstanceIdentifier nodeRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId())) + .toInstance(); + try { + statsPublisher.descriptionStatisticsUpdated(NodeMapping.toADNode(nodeRef), description); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, not reporting the update to publisher {}", nodeRef, statsPublisher, e); + } + } + } + } + + @Override + public ConcurrentMap> getNodeProps() { + final ConcurrentHashMap> props = new ConcurrentHashMap<>(); + final Nodes nodes = this.readOperAllMDNodes(); + for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node : nodes.getNode()) { + final FlowCapableNode fcn = node.getAugmentation(FlowCapableNode.class); + if (fcn != null) { + final ConcurrentHashMap perNodePropMap = new ConcurrentHashMap(); + final HashSet perNodeProps = NodeMapping.toADNodeProperties(fcn, node.getId()); + if (perNodeProps != null) { + for (final Property perNodeProp : perNodeProps) { + perNodePropMap.put(perNodeProp.getName(), perNodeProp); + } + } + + try { + final Node adNode = new Node(NodeMapping.MD_SAL_TYPE, NodeMapping.toADNodeId(node.getId())); + props.put(adNode, perNodePropMap); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, skipping it", node, e); + } + } + } + return props; + } + + private Nodes readOperAllMDNodes() { + final TypeSafeDataReader reader = TypeSafeDataReader.forReader(getDataService()); + return reader.readOperationalData(InstanceIdentifier.builder(Nodes.class).build()); + } + + @Override + public ConcurrentMap> getNodeConnectorProps(final Boolean refresh) { + final ConcurrentHashMap> props = new ConcurrentHashMap<>(); + for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node : this.readOperAllMDNodes().getNode()) { + for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc : node.getNodeConnector()) { + final FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class); + if (fcnc != null) { + final ConcurrentHashMap ncpsm = new ConcurrentHashMap<>(); + final HashSet ncps = NodeMapping.toADNodeConnectorProperties(fcnc); + if (ncps != null) { + for (final Property p : ncps) { + ncpsm.put(p.getName(), p); + } + } + + try { + props.put(NodeMapping.toADNodeConnector(nc.getId(), node.getId()), ncpsm); + } catch (ConstructionException e) { + LOG.warn("Failed to instantiate node {} connector {}, not reporting it", node.getId(), nc.getId(), e); + } + } + } + } + return props; + } + + private FlowCapableNodeConnector readOperFlowCapableNodeConnector(final NodeConnectorRef ref) { + final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc = + (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector) + getDataService().readOperationalData(ref.getValue()); + return nc.getAugmentation(FlowCapableNodeConnector.class); + } + + private static NodeConnectorStatistics toNodeConnectorStatistics(final org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.NodeConnectorStatistics nodeConnectorStatistics, final NodeId nodeId, final NodeConnectorId nodeConnectorId) throws ConstructionException { + final NodeConnectorStatistics it = new NodeConnectorStatistics(); + + final Packets packets = nodeConnectorStatistics.getPackets(); + it.setReceivePacketCount(packets.getReceived().longValue()); + it.setTransmitPacketCount(packets.getTransmitted().longValue()); + + final Bytes bytes = nodeConnectorStatistics.getBytes(); + it.setReceiveByteCount(bytes.getReceived().longValue()); + it.setTransmitByteCount(bytes.getTransmitted().longValue()); + + it.setReceiveDropCount(nodeConnectorStatistics.getReceiveDrops().longValue()); + it.setTransmitDropCount(nodeConnectorStatistics.getTransmitDrops().longValue()); + it.setReceiveErrorCount(nodeConnectorStatistics.getReceiveErrors().longValue()); + it.setTransmitErrorCount(nodeConnectorStatistics.getTransmitErrors().longValue()); + it.setReceiveFrameErrorCount(nodeConnectorStatistics.getReceiveFrameError().longValue()); + it.setReceiveOverRunErrorCount(nodeConnectorStatistics.getReceiveOverRunError().longValue()); + it.setReceiveCRCErrorCount(nodeConnectorStatistics.getReceiveCrcError().longValue()); + it.setCollisionCount(nodeConnectorStatistics.getCollisionCount().longValue()); + + final InstanceIdentifier nodeConnectorRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(nodeId)) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector.class, new NodeConnectorKey(nodeConnectorId)) + .build(); + it.setNodeConnector(NodeMapping.toADNodeConnector(new NodeConnectorRef(nodeConnectorRef))); + return it; + } + + private static NodeTableStatistics toNodeTableStatistics(final FlowTableStatistics tableStats, final Short tableId, final Node node) throws ConstructionException { + final NodeTableStatistics it = new NodeTableStatistics(); + it.setActiveCount(tableStats.getActiveFlows().getValue().intValue()); + it.setLookupCount(tableStats.getPacketsLookedUp().getValue().longValue()); + it.setMatchedCount(tableStats.getPacketsMatched().getValue().longValue()); + it.setName(tableId.toString()); + it.setNodeTable(new NodeTable(NodeMapping.MD_SAL_TYPE, tableId, node)); + return it; + } + + private NodeDescription toNodeDescription(final NodeRef nodeRef) { + final FlowCapableNode capableNode = this.readOperFlowCapableNode(nodeRef); + if (capableNode == null) { + return null; + } + + final NodeDescription it = new NodeDescription(); + it.setManufacturer(capableNode.getManufacturer()); + it.setSerialNumber(capableNode.getSerialNumber()); + it.setSoftware(capableNode.getSoftware()); + it.setDescription(capableNode.getDescription()); + return it; + } + + public Edge toADEdge(final Link link) throws ConstructionException { + NodeConnectorRef _source = link.getSource(); + NodeConnector _aDNodeConnector = NodeMapping.toADNodeConnector(_source); + NodeConnectorRef _destination = link.getDestination(); + NodeConnector _aDNodeConnector_1 = NodeMapping.toADNodeConnector(_destination); + Edge _edge = new Edge(_aDNodeConnector, _aDNodeConnector_1); + return _edge; + } + + /** + * OpendaylightFlowStatisticsListener interface implementation + */ + @Override + public void onAggregateFlowStatisticsUpdate(final AggregateFlowStatisticsUpdate notification) { + // Ignoring this notification as there does not seem to be a way to bubble this up to AD-SAL + } + + @Override + public void onFlowsStatisticsUpdate(final FlowsStatisticsUpdate notification) { + final ArrayList adsalFlowsStatistics = new ArrayList<>(); + final InstanceIdentifier nodeRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId())) + .build(); + + final Node aDNode; + try { + aDNode = NodeMapping.toADNode(nodeRef); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e); + return; + } + + for (final FlowAndStatisticsMapList flowStats : notification.getFlowAndStatisticsMapList()) { + if (flowStats.getTableId() == 0) { + adsalFlowsStatistics.add(InventoryAndReadAdapter.toFlowOnNode(flowStats, aDNode)); + } + } + for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) { + statsPublisher.nodeFlowStatisticsUpdated(aDNode, adsalFlowsStatistics); + } + } + + /** + * OpendaylightFlowTableStatisticsListener interface implementation + */ + @Override + public void onFlowTableStatisticsUpdate(final FlowTableStatisticsUpdate notification) { + ArrayList adsalFlowTableStatistics = new ArrayList<>(); + for (final FlowTableAndStatisticsMap stats : notification.getFlowTableAndStatisticsMap()) { + if (stats.getTableId().getValue() == 0) { + final NodeTableStatistics it = new NodeTableStatistics(); + it.setActiveCount(stats.getActiveFlows().getValue().intValue()); + it.setLookupCount(stats.getPacketsLookedUp().getValue().longValue()); + it.setMatchedCount(stats.getPacketsMatched().getValue().longValue()); + adsalFlowTableStatistics.add(it); + } + } + + final InstanceIdentifier nodeRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId())) + .build(); + + final Node aDNode; + try { + aDNode = NodeMapping.toADNode(nodeRef); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e); + return; + } + + for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) { + statsPublisher.nodeTableStatisticsUpdated(aDNode, adsalFlowTableStatistics); + } + } + + /** + * OpendaylightPortStatisticsUpdate interface implementation + */ + @Override + public void onNodeConnectorStatisticsUpdate(final NodeConnectorStatisticsUpdate notification) { + final ArrayList adsalPortStatistics = new ArrayList(); + for (final NodeConnectorStatisticsAndPortNumberMap nodeConnectorStatistics : notification.getNodeConnectorStatisticsAndPortNumberMap()) { + try { + adsalPortStatistics.add(toNodeConnectorStatistics( + nodeConnectorStatistics, notification.getId(), nodeConnectorStatistics.getNodeConnectorId())); + } catch (ConstructionException e) { + LOG.warn("Failed to create statistics for node {} connector {}, not updating them", + notification.getId(), nodeConnectorStatistics.getNodeConnectorId(), e); + } + } + + final InstanceIdentifier nodeRef = + InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId())) + .build(); + + final Node aDNode; + try { + aDNode = NodeMapping.toADNode(nodeRef); + } catch (ConstructionException e) { + LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e); + return; + } + + for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) { + statsPublisher.nodeConnectorStatisticsUpdated(aDNode, adsalPortStatistics); + } + } + + private static FlowOnNode toFlowOnNode(final FlowAndStatisticsMapList flowAndStatsMap, final Node node) { + final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flowAndStatsMap, node)); + return addFlowStats(it, flowAndStatsMap); + } + + private static FlowOnNode addFlowStats(final FlowOnNode node, final GenericStatistics stats) { + node.setByteCount(stats.getByteCount().getValue().longValue()); + node.setPacketCount(stats.getPacketCount().getValue().longValue()); + node.setDurationSeconds(stats.getDuration().getSecond().getValue().intValue()); + node.setDurationNanoseconds(stats.getDuration().getNanosecond().getValue().intValue()); + return node; + } + + @Override + public Set getConfiguredNotConnectedNodes() { + return Collections.emptySet(); + } + + private void publishNodeUpdate(final Node node, final UpdateType updateType, final Set properties) { + for (final IPluginOutInventoryService publisher : getInventoryPublisher()) { + publisher.updateNode(node, updateType, properties); + } + } + + private void publishNodeConnectorUpdate(final NodeConnector nodeConnector, final UpdateType updateType, final Set properties) { + for (final IPluginOutInventoryService publisher : getInventoryPublisher()) { + publisher.updateNodeConnector(nodeConnector, updateType, properties); + } + } + + private boolean isKnownNodeConnector(final InstanceIdentifier nodeConnectorIdentifier) { + final List path = nodeConnectorIdentifier.getPath(); + if (path.size() >= 3) { + final PathArgument nodePath = path.get(1); + final PathArgument nodeConnectorPath = path.get(2); + final List nodeConnectors = nodeToNodeConnectorsMap.get(nodePath); + if (nodeConnectors != null) { + return nodeConnectors.contains(nodeConnectorPath); + } + } + return false; + } + + private boolean recordNodeConnector(final InstanceIdentifier nodeConnectorIdentifier) { + final List path = nodeConnectorIdentifier.getPath(); + if (path.size() < 3) { + return false; + } + + final PathArgument nodePath = path.get(1); + final PathArgument nodeConnectorPath = path.get(2); + + synchronized (this) { + List nodeConnectors = this.nodeToNodeConnectorsMap.get(nodePath); + if (nodeConnectors == null) { + nodeConnectors = new ArrayList<>(); + this.nodeToNodeConnectorsMap.put(nodePath, nodeConnectors); + } + + return nodeConnectors.add(nodeConnectorPath); + } + } + + private List removeNodeConnectors(final InstanceIdentifier nodeIdentifier) { + return this.nodeToNodeConnectorsMap.remove(nodeIdentifier.getPath().get(1)); + } +} diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend deleted file mode 100644 index 8908504f15..0000000000 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.compatibility - -import java.util.ArrayList -import java.util.Collections -import java.util.List -import java.util.Map -import java.util.Set -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CopyOnWriteArrayList -import java.util.concurrent.locks.Lock -import java.util.concurrent.locks.ReentrantLock -import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader -import org.opendaylight.controller.sal.binding.api.data.DataBrokerService -import org.opendaylight.controller.sal.binding.api.data.DataProviderService -import org.opendaylight.controller.sal.core.Edge -import org.opendaylight.controller.sal.core.Node -import org.opendaylight.controller.sal.core.NodeTable -import org.opendaylight.controller.sal.core.UpdateType -import org.opendaylight.controller.sal.flowprogrammer.Flow -import org.opendaylight.controller.sal.inventory.IPluginInInventoryService -import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService -import org.opendaylight.controller.sal.reader.FlowOnNode -import org.opendaylight.controller.sal.reader.IPluginInReadService -import org.opendaylight.controller.sal.reader.IPluginOutReadService -import org.opendaylight.controller.sal.reader.NodeConnectorStatistics -import org.opendaylight.controller.sal.reader.NodeDescription -import org.opendaylight.controller.sal.reader.NodeTableStatistics -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInputBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsListener -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.Link -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatistics -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllNodeConnectorsStatisticsInputBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetNodeConnectorStatisticsInputBuilder -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener -import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService -import org.opendaylight.yangtools.yang.binding.DataObject -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import org.slf4j.LoggerFactory - -import static extension org.opendaylight.controller.sal.common.util.Arguments.* -import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.* - -class InventoryAndReadAdapter implements IPluginInReadService, - IPluginInInventoryService, - OpendaylightInventoryListener, - OpendaylightFlowStatisticsListener, - OpendaylightFlowTableStatisticsListener, - OpendaylightPortStatisticsListener { - - private static val LOG = LoggerFactory.getLogger(InventoryAndReadAdapter); - - private static val OPENFLOWV10_TABLE_ID = new Integer(0).shortValue; - @Property - DataBrokerService dataService; - - @Property - DataProviderService dataProviderService; - - @Property - OpendaylightFlowStatisticsService flowStatisticsService; - - @Property - OpendaylightPortStatisticsService nodeConnectorStatisticsService; - - @Property - OpendaylightFlowTableStatisticsService flowTableStatisticsService; - - @Property - FlowTopologyDiscoveryService topologyDiscovery; - - @Property - List statisticsPublisher = new CopyOnWriteArrayList(); - - @Property - List inventoryPublisher = new CopyOnWriteArrayList(); - - private final InventoryNotificationProvider inventoryNotificationProvider = new InventoryNotificationProvider(); - - private final Map> nodeToNodeConnectorsMap = new ConcurrentHashMap>(); - - private final Lock nodeToNodeConnectorsLock = new ReentrantLock(); - - - def startAdapter(){ - inventoryNotificationProvider.dataProviderService = dataProviderService; - inventoryNotificationProvider.inventoryPublisher = inventoryPublisher; - // inventoryNotificationProvider.start(); - } - - def start(){ - } - - def setInventoryPublisher(IPluginOutInventoryService listener){ - inventoryPublisher.add(listener); - } - - def unsetInventoryPublisher(IPluginOutInventoryService listener){ - inventoryPublisher.remove(listener); - } - - def setReadPublisher(IPluginOutReadService listener) { - statisticsPublisher.add(listener); - } - - def unsetReadPublisher (IPluginOutReadService listener) { - if( listener != null) - statisticsPublisher.remove(listener); - } - - protected def startChange() { - return dataProviderService.beginTransaction; - } - - override getTransmitRate(org.opendaylight.controller.sal.core.NodeConnector connector) { - val nodeConnector = readFlowCapableNodeConnector(connector.toNodeConnectorRef); - return nodeConnector.currentSpeed - } - - override readAllFlow(Node node, boolean cached) { - - val output = new ArrayList(); - val tableRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node)) - .augmentation(FlowCapableNode).child(Table, new TableKey(OPENFLOWV10_TABLE_ID)).toInstance(); - - val it = this.startChange(); - - val table= it.readConfigurationData(tableRef) as Table; - - if(table != null){ - LOG.trace("Number of flows installed in table 0 of node {} : {}",node,table.flow.size); - - for(flow : table.flow){ - - val adsalFlow = ToSalConversionsUtils.toFlow(flow,node); - val statsFromDataStore = flow.getAugmentation(FlowStatisticsData); - - if(statsFromDataStore != null){ - val it = new FlowOnNode(adsalFlow); - byteCount = statsFromDataStore.flowStatistics.byteCount.value.longValue; - packetCount = statsFromDataStore.flowStatistics.packetCount.value.longValue; - durationSeconds = statsFromDataStore.flowStatistics.duration.second.value.intValue; - durationNanoseconds = statsFromDataStore.flowStatistics.duration.nanosecond.value.intValue; - - output.add(it); - } - } - } - - //TODO (main): Shell we send request to the switch? It will make async request to the switch. - // Once plugin receive response, it will let adaptor know through onFlowStatisticsUpdate() - // If we assume that md-sal statistics manager will always be running, then its not required - // But if not, then sending request will collect the latest data for adaptor atleast. - val input = new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder; - input.setNode(node.toNodeRef); - flowStatisticsService.getAllFlowsStatisticsFromAllFlowTables(input.build) - - return output; - } - - override readAllNodeConnector(Node node, boolean cached) { - - val ret = new ArrayList(); - val nodeRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node)) - .toInstance(); - - val provider = this.startChange(); - - val dsNode= provider.readConfigurationData(nodeRef) as org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; - - if(dsNode != null){ - - for (dsNodeConnector : dsNode.nodeConnector){ - val nodeConnectorRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node)) - .child(NodeConnector, dsNodeConnector.key) - .toInstance(); - - val nodeConnectorFromDS = provider.readConfigurationData(nodeConnectorRef) as NodeConnector; - - if(nodeConnectorFromDS != null){ - val nodeConnectorStatsFromDs = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData) as FlowCapableNodeConnectorStatistics; - - ret.add(toNodeConnectorStatistics(nodeConnectorStatsFromDs.flowCapableNodeConnectorStatistics,dsNode.id,dsNodeConnector.id)); - } - } - } - - //TODO: Refer TODO (main) - val input = new GetAllNodeConnectorsStatisticsInputBuilder(); - input.setNode(node.toNodeRef); - nodeConnectorStatisticsService.getAllNodeConnectorsStatistics(input.build()); - return ret; - } - - override readAllNodeTable(Node node, boolean cached) { - val ret = new ArrayList(); - - val dsFlowCapableNode= readFlowCapableNode(node.toNodeRef) - - if(dsFlowCapableNode != null){ - - for (table : dsFlowCapableNode.table){ - - val tableStats = table.getAugmentation(FlowTableStatisticsData); - - if(tableStats != null){ - ret.add(toNodeTableStatistics(tableStats.flowTableStatistics,table.id,node)); - } - } - } - - //TODO: Refer TODO (main) - val input = new GetFlowTablesStatisticsInputBuilder(); - input.setNode(node.toNodeRef); - flowTableStatisticsService.getFlowTablesStatistics(input.build); - return ret; - } - - override readDescription(Node node, boolean cached) { - return toNodeDescription(node.toNodeRef); - } - - override readFlow(Node node, Flow targetFlow, boolean cached) { - var FlowOnNode ret= null; - - val tableRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node)) - .augmentation(FlowCapableNode).child(Table, new TableKey(OPENFLOWV10_TABLE_ID)).toInstance(); - - val it = this.startChange(); - - val table= it.readConfigurationData(tableRef) as Table; - - if(table != null){ - LOG.trace("Number of flows installed in table 0 of node {} : {}",node,table.flow.size); - - for(mdsalFlow : table.flow){ - if(FromSalConversionsUtils.flowEquals(mdsalFlow, MDFlowMapping.toMDSalflow(targetFlow))){ - val statsFromDataStore = mdsalFlow.getAugmentation(FlowStatisticsData); - - if(statsFromDataStore != null){ - LOG.debug("Found matching flow in the data store flow table "); - val it = new FlowOnNode(targetFlow); - byteCount = statsFromDataStore.flowStatistics.byteCount.value.longValue; - packetCount = statsFromDataStore.flowStatistics.packetCount.value.longValue; - durationSeconds = statsFromDataStore.flowStatistics.duration.second.value.intValue; - durationNanoseconds = statsFromDataStore.flowStatistics.duration.nanosecond.value.intValue; - - ret = it; - } - } - } - } - - //TODO: Refer TODO (main) - val input = new GetFlowStatisticsFromFlowTableInputBuilder; - input.setNode(node.toNodeRef); - input.fieldsFrom(MDFlowMapping.toMDSalflow(targetFlow)); - flowStatisticsService.getFlowStatisticsFromFlowTable(input.build) - - return ret; - - } - - override readNodeConnector(org.opendaylight.controller.sal.core.NodeConnector connector, boolean cached) { - var NodeConnectorStatistics nodeConnectorStatistics = null; - - val nodeConnectorRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(connector.node)) - .child(NodeConnector, InventoryMapping.toNodeConnectorKey(connector)) - .toInstance(); - val provider = this.startChange(); - - val nodeConnectorFromDS = provider.readConfigurationData(nodeConnectorRef) as NodeConnector; - - if(nodeConnectorFromDS != null){ - val nodeConnectorStatsFromDs = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData) as FlowCapableNodeConnectorStatistics; - if(nodeConnectorStatsFromDs != null) { - nodeConnectorStatistics = toNodeConnectorStatistics(nodeConnectorStatsFromDs.flowCapableNodeConnectorStatistics, - InventoryMapping.toNodeKey(connector.node).id, - InventoryMapping.toNodeConnectorKey(connector).id); - } - } - - //TODO: Refer TODO (main) - val input = new GetNodeConnectorStatisticsInputBuilder(); - input.setNode(connector.node.toNodeRef); - input.setNodeConnectorId(InventoryMapping.toNodeConnectorKey(connector).id); - nodeConnectorStatisticsService.getNodeConnectorStatistics(input.build()); - return nodeConnectorStatistics; - } - - override readNodeTable(NodeTable nodeTable, boolean cached) { - var NodeTableStatistics nodeStats = null - - val tableRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(nodeTable.node)) - .augmentation(FlowCapableNode).child(Table, new TableKey(nodeTable.ID as Short)).toInstance(); - - val it = this.startChange(); - - val table= it.readConfigurationData(tableRef) as Table; - - if(table != null){ - val tableStats = table.getAugmentation(FlowTableStatisticsData); - - if(tableStats != null){ - nodeStats = toNodeTableStatistics(tableStats.flowTableStatistics,table.id,nodeTable.node); - } - } - - //TODO: Refer TODO (main) - val input = new GetFlowTablesStatisticsInputBuilder(); - input.setNode(nodeTable.node.toNodeRef); - flowTableStatisticsService.getFlowTablesStatistics(input.build); - - return nodeStats; - } - - override onNodeConnectorRemoved(NodeConnectorRemoved update) { - // Never received - } - - override onNodeRemoved(NodeRemoved notification) { - val properties = Collections.emptySet(); - - removeNodeConnectors(notification.nodeRef.value); - - publishNodeUpdate(notification.nodeRef.toADNode, UpdateType.REMOVED, properties); - } - - override onNodeConnectorUpdated(NodeConnectorUpdated update) { - var updateType = UpdateType.CHANGED; - if(!isKnownNodeConnector(update.nodeConnectorRef.value)){ - updateType = UpdateType.ADDED; - recordNodeConnector(update.nodeConnectorRef.value); - } - - var nodeConnector = update.nodeConnectorRef.toADNodeConnector - - publishNodeConnectorUpdate(nodeConnector , updateType , update.toADNodeConnectorProperties); - } - - override onNodeUpdated(NodeUpdated notification) { - val InstanceIdentifier identifier = notification.nodeRef.value as InstanceIdentifier; - - var updateType = UpdateType.CHANGED; - if ( this._dataService.readOperationalData(identifier) == null ){ - updateType = UpdateType.ADDED; - } - publishNodeUpdate(notification.nodeRef.toADNode, updateType, notification.toADNodeProperties); - - //Notify the listeners of IPluginOutReadService - - for (statsPublisher : statisticsPublisher){ - val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance; - val description = notification.nodeRef.toNodeDescription - if(description != null) { - statsPublisher.descriptionStatisticsUpdated(nodeRef.toADNode,description); - } - } - } - - override getNodeProps() { - val props = new ConcurrentHashMap>() - - val nodes = readAllMDNodes() - for (node : nodes.node ) { - val fcn = node.getAugmentation(FlowCapableNode) - if(fcn != null) { - val perNodeProps = fcn.toADNodeProperties(node.id) - val perNodePropMap = new ConcurrentHashMap - if(perNodeProps != null ) { - for(perNodeProp : perNodeProps) { - perNodePropMap.put(perNodeProp.name,perNodeProp) - } - } - props.put(new Node(MD_SAL_TYPE, node.id.toADNodeId),perNodePropMap) - } - } - return props; - } - - private def readAllMDNodes() { - val nodesRef = InstanceIdentifier.builder(Nodes) - .toInstance - val reader = TypeSafeDataReader.forReader(dataService) - return reader.readOperationalData(nodesRef) - } - - private def readAllMDNodeConnectors(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node) { - val nodeRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(node.id)) - .toInstance - val reader = TypeSafeDataReader.forReader(dataService) - return reader.readOperationalData(nodeRef).nodeConnector - } - - override getNodeConnectorProps(Boolean refresh) { - // Note, because the MD-SAL has a unified data store, we can ignore the Boolean refresh, as we have no secondary - // data store to refresh from - val props = new ConcurrentHashMap>() - val nodes = readAllMDNodes() - for (node : nodes.node) { - val ncs = node.readAllMDNodeConnectors - if(ncs != null) { - for( nc : ncs ) { - val fcnc = nc.getAugmentation(FlowCapableNodeConnector) - if(fcnc != null) { - val ncps = fcnc.toADNodeConnectorProperties - val ncpsm = new ConcurrentHashMap - if(ncps != null) { - for(p : ncps) { - ncpsm.put(p.name,p) - } - } - props.put(nc.id.toADNodeConnector(node.id),ncpsm) - } - } - } - } - return props - } - - private def FlowCapableNode readFlowCapableNode(NodeRef ref) { - val dataObject = dataService.readOperationalData(ref.value as InstanceIdentifier); - if(dataObject != null) { - val node = dataObject.checkInstanceOf( - org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node); - return node.getAugmentation(FlowCapableNode); - } - return null; - } - - private def FlowCapableNodeConnector readFlowCapableNodeConnector(NodeConnectorRef ref) { - val dataObject = dataService.readOperationalData(ref.value as InstanceIdentifier); - val node = dataObject.checkInstanceOf( - NodeConnector); - return node.getAugmentation(FlowCapableNodeConnector); - } - - private def toNodeConnectorStatistics( - org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.NodeConnectorStatistics nodeConnectorStatistics, NodeId nodeId, NodeConnectorId nodeConnectorId) { - - val it = new NodeConnectorStatistics(); - - receivePacketCount = nodeConnectorStatistics.packets.received.longValue; - transmitPacketCount = nodeConnectorStatistics.packets.transmitted.longValue; - - receiveByteCount = nodeConnectorStatistics.bytes.received.longValue; - transmitByteCount = nodeConnectorStatistics.bytes.transmitted.longValue; - - receiveDropCount = nodeConnectorStatistics.receiveDrops.longValue; - transmitDropCount = nodeConnectorStatistics.transmitDrops.longValue; - - receiveErrorCount = nodeConnectorStatistics.receiveErrors.longValue; - transmitErrorCount = nodeConnectorStatistics.transmitErrors.longValue; - - receiveFrameErrorCount = nodeConnectorStatistics.receiveFrameError.longValue; - receiveOverRunErrorCount = nodeConnectorStatistics.receiveOverRunError.longValue; - receiveCRCErrorCount = nodeConnectorStatistics.receiveCrcError.longValue; - collisionCount = nodeConnectorStatistics.collisionCount.longValue; - - val nodeConnectorRef = InstanceIdentifier.builder(Nodes) - .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(nodeId)) - .child(NodeConnector,new NodeConnectorKey(nodeConnectorId)).toInstance; - - nodeConnector = NodeMapping.toADNodeConnector(new NodeConnectorRef(nodeConnectorRef)); - - return it; - } - - private def toNodeTableStatistics( - FlowTableStatistics tableStats, - Short tableId,Node node){ - var it = new NodeTableStatistics(); - - activeCount = tableStats.activeFlows.value.intValue; - lookupCount = tableStats.packetsLookedUp.value.intValue; - matchedCount = tableStats.packetsMatched.value.intValue; - name = tableId.toString; - nodeTable = new NodeTable(NodeMapping.MD_SAL_TYPE,tableId,node); - return it; - } - - private def toNodeDescription(NodeRef nodeRef){ - val capableNode = readFlowCapableNode(nodeRef); - if(capableNode !=null) { - val it = new NodeDescription() - manufacturer = capableNode.manufacturer - serialNumber = capableNode.serialNumber - software = capableNode.software - description = capableNode.description - - return it; - } - return null; - } - - - def Edge toADEdge(Link link) { - new Edge(link.source.toADNodeConnector,link.destination.toADNodeConnector) - } - - /* - * OpendaylightFlowStatisticsListener interface implementation - */ - override onAggregateFlowStatisticsUpdate(AggregateFlowStatisticsUpdate notification) { - //Ignoring this notification as there does not seem to be a way to bubble this up to AD-SAL - } - - override onFlowsStatisticsUpdate(FlowsStatisticsUpdate notification) { - - val adsalFlowsStatistics = new ArrayList(); - val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance; - - for(flowStats : notification.flowAndStatisticsMapList){ - if(flowStats.tableId == 0) - adsalFlowsStatistics.add(toFlowOnNode(flowStats,nodeRef.toADNode)); - } - - for (statsPublisher : statisticsPublisher){ - statsPublisher.nodeFlowStatisticsUpdated(nodeRef.toADNode,adsalFlowsStatistics); - } - - } - /* - * OpendaylightFlowTableStatisticsListener interface implementation - */ - override onFlowTableStatisticsUpdate(FlowTableStatisticsUpdate notification) { - var adsalFlowTableStatistics = new ArrayList(); - - for(stats : notification.flowTableAndStatisticsMap){ - if (stats.tableId.value == 0){ - val it = new NodeTableStatistics(); - activeCount = stats.activeFlows.value.intValue; - lookupCount = stats.packetsLookedUp.value.longValue; - matchedCount = stats.packetsMatched.value.longValue; - - adsalFlowTableStatistics.add(it); - } - } - for (statsPublisher : statisticsPublisher){ - val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance; - statsPublisher.nodeTableStatisticsUpdated(nodeRef.toADNode,adsalFlowTableStatistics); - } - } - - /* - * OpendaylightPortStatisticsUpdate interface implementation - */ - override onNodeConnectorStatisticsUpdate(NodeConnectorStatisticsUpdate notification) { - - val adsalPortStatistics = new ArrayList(); - - for(nodeConnectorStatistics : notification.nodeConnectorStatisticsAndPortNumberMap){ - adsalPortStatistics.add(toNodeConnectorStatistics(nodeConnectorStatistics,notification.id,nodeConnectorStatistics.nodeConnectorId)); - } - - for (statsPublisher : statisticsPublisher){ - val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance; - statsPublisher.nodeConnectorStatisticsUpdated(nodeRef.toADNode,adsalPortStatistics); - } - - } - - private static def toFlowOnNode (FlowAndStatisticsMapList flowAndStatsMap,Node node){ - - val it = new FlowOnNode(ToSalConversionsUtils.toFlow(flowAndStatsMap,node)); - - byteCount = flowAndStatsMap.byteCount.value.longValue; - packetCount = flowAndStatsMap.packetCount.value.longValue; - durationSeconds = flowAndStatsMap.duration.second.value.intValue; - durationNanoseconds = flowAndStatsMap.duration.nanosecond.value.intValue; - - return it; - } - - override getConfiguredNotConnectedNodes() { - return Collections.emptySet(); - } - - - private def publishNodeUpdate(Node node, UpdateType updateType, Set properties){ - for( publisher : inventoryPublisher){ - publisher.updateNode(node, updateType, properties); - } - } - - private def publishNodeConnectorUpdate(org.opendaylight.controller.sal.core.NodeConnector nodeConnector, UpdateType updateType, Set properties){ - for( publisher : inventoryPublisher){ - publisher.updateNodeConnector(nodeConnector, updateType, properties); - } - } - - private def isKnownNodeConnector(InstanceIdentifier nodeConnectorIdentifier){ - if(nodeConnectorIdentifier.path.size() < 3) { - return false; - } - - val nodePath = nodeConnectorIdentifier.path.get(1); - val nodeConnectorPath = nodeConnectorIdentifier.getPath().get(2); - - val nodeConnectors = nodeToNodeConnectorsMap.get(nodePath); - - if(nodeConnectors == null){ - return false; - } - return nodeConnectors.contains(nodeConnectorPath); - } - - - private def recordNodeConnector(InstanceIdentifier nodeConnectorIdentifier){ - if(nodeConnectorIdentifier.path.size() < 3) { - return false; - } - - val nodePath = nodeConnectorIdentifier.path.get(1); - val nodeConnectorPath = nodeConnectorIdentifier.getPath().get(2); - - nodeToNodeConnectorsLock.lock(); - - try { - var nodeConnectors = nodeToNodeConnectorsMap.get(nodePath); - - if(nodeConnectors == null){ - nodeConnectors = new ArrayList(); - nodeToNodeConnectorsMap.put(nodePath, nodeConnectors); - } - - nodeConnectors.add(nodeConnectorPath); - } finally { - nodeToNodeConnectorsLock.unlock(); - } - } - - private def removeNodeConnectors(InstanceIdentifier nodeIdentifier){ - val nodePath = nodeIdentifier.path.get(1); - - nodeToNodeConnectorsMap.remove(nodePath); - } -} diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.java index 29904220d7..1e4d97446f 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.java @@ -1,104 +1,99 @@ /** * Copyright (c) 2013 Cisco Systems, 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.controller.sal.compatibility; -import org.eclipse.xtend2.lib.StringConcatenation; -import org.eclipse.xtext.xbase.lib.Conversions; -import org.eclipse.xtext.xbase.lib.IterableExtensions; -import org.opendaylight.controller.sal.core.Node; -import org.opendaylight.controller.sal.core.NodeConnector; +import java.util.Iterator; + import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; - -import java.util.List; - -@SuppressWarnings("all") -public class InventoryMapping { - public static NodeConnector toAdNodeConnector(final InstanceIdentifier identifier) { - final List path = identifier.getPath(); - final PathArgument lastPathArgument = IterableExtensions.last(path); - final NodeConnectorKey tpKey = ((IdentifiableItem) lastPathArgument).getKey(); - return InventoryMapping.nodeConnectorFromId(tpKey.getId().getValue()); - } - - public static Node toAdNode(final InstanceIdentifier identifier) { - final List path = identifier.getPath(); - final PathArgument lastPathArgument = IterableExtensions.last(path); - final NodeKey tpKey = ((IdentifiableItem) lastPathArgument).getKey(); - return InventoryMapping.nodeFromNodeId(tpKey.getId().getValue()); - } - - public static NodeRef toNodeRef(final Node node) { - final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node)); - final NodeKey nodeKey = new NodeKey(nodeId); - final InstanceIdentifierBuilder nodes = InstanceIdentifier.builder(Nodes.class); - final InstanceIdentifierBuilder child = - nodes.child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey); - final InstanceIdentifier path = child.toInstance(); - return new NodeRef(path); - } - - public static NodeKey toNodeKey(final Node node) { - final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node)); - return new NodeKey(nodeId); - } - - public static NodeConnectorKey toNodeConnectorKey(final NodeConnector nc) { - final NodeConnectorId nodeConnectorId = new NodeConnectorId(InventoryMapping.toNodeConnectorId(nc)); - return new NodeConnectorKey(nodeConnectorId); - } - - public static String toNodeId(final Node node) { - final StringConcatenation builder = new StringConcatenation(); - builder.append("ad-sal:"); - builder.append(node.getType(), ""); - builder.append("::"); - builder.append(node.getNodeIDString(), ""); - return builder.toString(); - } - - public static String toNodeConnectorId(final NodeConnector nc) { - final StringConcatenation builder = new StringConcatenation(); - builder.append(InventoryMapping.toNodeId(nc.getNode()), ""); - builder.append("::"); - builder.append(nc.getNodeConnectorIDString(), ""); - return builder.toString(); - } - - public static Node nodeFromNodeId(final String nodeId) { - final String[] split = nodeId.split("::"); - return InventoryMapping.nodeFromString(split); - } - - public static NodeConnector nodeConnectorFromId(final String invId) { - final String[] split = invId.split("::"); - return InventoryMapping.nodeConnectorFromString(split); - } - - private static NodeConnector nodeConnectorFromString(final String[] string) { - final List subList = ((List)Conversions.doWrapArray(string)).subList(0, 1); - final Node node = InventoryMapping.nodeFromString(((String[])Conversions.unwrapArray(subList, String.class))); - final String index3 = string[2]; - return NodeConnector.fromStringNoNode(index3, node); - } - - private static Node nodeFromString(final String[] strings) { - String index0 = strings[0]; - final String type = index0.substring(6); - String id = strings[1]; - return Node.fromString(type, id); - } +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +import com.google.common.base.Splitter; + +public final class InventoryMapping { + private static final String NODE_TYPE_STRING = "::"; + private static final Splitter NODE_TYPE_SPLITTER = Splitter.on(NODE_TYPE_STRING); + + private InventoryMapping() { + throw new UnsupportedOperationException("Utility class"); + } + + public static org.opendaylight.controller.sal.core.NodeConnector toAdNodeConnector(final InstanceIdentifier identifier) { + @SuppressWarnings("unchecked") + final NodeConnectorKey tpKey = ((KeyedInstanceIdentifier) identifier).getKey(); + return InventoryMapping.nodeConnectorFromId(tpKey.getId().getValue()); + } + + public static org.opendaylight.controller.sal.core.Node toAdNode(final InstanceIdentifier identifier) { + @SuppressWarnings("unchecked") + final NodeKey tpKey = ((KeyedInstanceIdentifier)identifier).getKey(); + return InventoryMapping.nodeFromNodeId(tpKey.getId().getValue()); + } + + public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) { + final NodeKey nodeKey = new NodeKey(new NodeId(InventoryMapping.toNodeId(node))); + final InstanceIdentifier path = InstanceIdentifier.builder(Nodes.class) + .child(Node.class, nodeKey).toInstance(); + return new NodeRef(path); + } + + public static NodeKey toNodeKey(final org.opendaylight.controller.sal.core.Node node) { + final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node)); + return new NodeKey(nodeId); + } + + public static NodeConnectorKey toNodeConnectorKey(final org.opendaylight.controller.sal.core.NodeConnector nc) { + final NodeConnectorId nodeConnectorId = new NodeConnectorId(InventoryMapping.toNodeConnectorId(nc)); + return new NodeConnectorKey(nodeConnectorId); + } + + private static StringBuilder nodeIdBulder(final org.opendaylight.controller.sal.core.Node node) { + final StringBuilder sb = new StringBuilder(); + sb.append("ad-sal:"); + sb.append(node.getType()); + sb.append(NODE_TYPE_STRING); + sb.append(node.getNodeIDString()); + return sb; + } + + public static String toNodeId(final org.opendaylight.controller.sal.core.Node node) { + return nodeIdBulder(node).toString(); + } + + public static String toNodeConnectorId(final org.opendaylight.controller.sal.core.NodeConnector nc) { + final StringBuilder sb = nodeIdBulder(nc.getNode()); + sb.append(NODE_TYPE_STRING); + sb.append(nc.getNodeConnectorIDString()); + return sb.toString(); + } + + public static org.opendaylight.controller.sal.core.Node nodeFromNodeId(final String nodeId) { + return InventoryMapping.nodeFromStrings(NODE_TYPE_SPLITTER.split(nodeId).iterator()); + } + + public static org.opendaylight.controller.sal.core.NodeConnector nodeConnectorFromId(final String invId) { + return InventoryMapping.nodeConnectorFromString(NODE_TYPE_SPLITTER.split(invId).iterator()); + } + + private static org.opendaylight.controller.sal.core.NodeConnector nodeConnectorFromString(final Iterator it) { + final org.opendaylight.controller.sal.core.Node node = InventoryMapping.nodeFromStrings(it); + return org.opendaylight.controller.sal.core.NodeConnector.fromStringNoNode(it.next(), node); + } + + private static org.opendaylight.controller.sal.core.Node nodeFromStrings(final Iterator it) { + final String type = it.next().substring(6); + return org.opendaylight.controller.sal.core.Node.fromString(type, it.next()); + } } diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java index 2c95252ac7..ba86ad99fb 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java @@ -61,314 +61,314 @@ import com.google.common.base.Objects; import com.google.common.base.Preconditions; public final class NodeMapping { - public final static String MD_SAL_TYPE = "MD_SAL"; - - private final static Class NODE_CLASS = Node.class; - - private final static Class NODECONNECTOR_CLASS = NodeConnector.class; - - private NodeMapping() { - throw new UnsupportedOperationException("Utility class. Instantiation is not allowed."); - } - - public static org.opendaylight.controller.sal.core.Node toADNode(final InstanceIdentifier node) throws ConstructionException { - NodeId nodeId = NodeMapping.toNodeId(node); - return NodeMapping.toADNode(nodeId); - } - - public static org.opendaylight.controller.sal.core.Node toADNode(final NodeId id) throws ConstructionException { - String aDNodeId = NodeMapping.toADNodeId(id); - return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, aDNodeId); - } - - public static NodeId toNodeId(final InstanceIdentifier id) { - final NodeKey key = id.firstKeyOf(Node.class, NodeKey.class); - Preconditions.checkArgument(key != null, "No node identifier found in %s", id); - return key.getId(); - } - - public static String toADNodeId(final NodeId nodeId) { - return nodeId.getValue(); - } - - public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorRef source) throws ConstructionException { - final InstanceIdentifier id = Preconditions.checkNotNull(source.getValue()); - final NodeConnectorKey key = id.firstKeyOf(NodeConnector.class, NodeConnectorKey.class); - return NodeMapping.toADNodeConnector(key.getId(), NodeMapping.toNodeId(id)); - } - - public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorId ncid, final NodeId nid) throws ConstructionException { - String nodeConnectorType = NodeMapping.toNodeConnectorType(ncid, nid); - Object aDNodeConnectorId = NodeMapping.toADNodeConnectorId(ncid, nid); - org.opendaylight.controller.sal.core.Node aDNode = NodeMapping.toADNode(nid); - return new org.opendaylight.controller.sal.core.NodeConnector(nodeConnectorType, aDNodeConnectorId, aDNode); - } - - public static String toNodeConnectorType(final NodeConnectorId ncId, final NodeId nodeId) { - if (ncId.equals(toLocalNodeConnectorId(nodeId))) { - return NodeConnectorIDType.SWSTACK; - } else if (ncId.equals(toNormalNodeConnectorId(nodeId))) { - return NodeConnectorIDType.HWPATH; - } else if (ncId.equals(toControllerNodeConnectorId(nodeId))) { - return NodeConnectorIDType.CONTROLLER; - } - return MD_SAL_TYPE; - } - - public static Object toADNodeConnectorId(final NodeConnectorId nodeConnectorId, final NodeId nodeId) { - if (nodeConnectorId.equals(toLocalNodeConnectorId(nodeId)) || - nodeConnectorId.equals(toNormalNodeConnectorId(nodeId)) || - nodeConnectorId.equals(toControllerNodeConnectorId(nodeId))) { - return org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID; - } - return nodeConnectorId.getValue(); - } - - public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) { - return new NodeConnectorId(node.getValue() + ":" + 4294967293L); - } - - public static NodeConnectorId toLocalNodeConnectorId(final NodeId node) { - return new NodeConnectorId(node.getValue() + ":" + 4294967294L); - } - - public static NodeConnectorId toNormalNodeConnectorId(final NodeId node) { - return new NodeConnectorId(node.getValue() + ":" + 4294967290L); - } - - public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) { - Preconditions.checkArgument(MD_SAL_TYPE.equals(node.getType())); - final String nodeId = Arguments.checkInstanceOf(node.getID(), String.class); - final NodeKey nodeKey = new NodeKey(new NodeId(nodeId)); - final InstanceIdentifier nodePath = InstanceIdentifier.builder(Nodes.class).child(NODE_CLASS, nodeKey).toInstance(); - return new NodeRef(nodePath); - } - - public static NodeConnectorRef toNodeConnectorRef(final org.opendaylight.controller.sal.core.NodeConnector nodeConnector) { - - final NodeRef node = NodeMapping.toNodeRef(nodeConnector.getNode()); - @SuppressWarnings("unchecked") - final InstanceIdentifier nodePath = ((InstanceIdentifier) node.getValue()); - NodeConnectorId nodeConnectorId = null; - - if (nodeConnector.getID().equals(org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID)) { - final NodeId nodeId = toNodeId(nodePath); - final String nodeConnectorType = nodeConnector.getType(); - if (nodeConnectorType.equals(NodeConnectorIDType.SWSTACK)) { - nodeConnectorId = toLocalNodeConnectorId(nodeId); - } else if (nodeConnectorType.equals(NodeConnectorIDType.HWPATH)) { - nodeConnectorId = toNormalNodeConnectorId(nodeId); - } else if (nodeConnectorType.equals(NodeConnectorIDType.CONTROLLER)) { - nodeConnectorId = toControllerNodeConnectorId(nodeId); + public final static String MD_SAL_TYPE = "MD_SAL"; + + private final static Class NODE_CLASS = Node.class; + + private final static Class NODECONNECTOR_CLASS = NodeConnector.class; + + private NodeMapping() { + throw new UnsupportedOperationException("Utility class. Instantiation is not allowed."); + } + + public static org.opendaylight.controller.sal.core.Node toADNode(final InstanceIdentifier node) throws ConstructionException { + NodeId nodeId = NodeMapping.toNodeId(node); + return NodeMapping.toADNode(nodeId); + } + + public static org.opendaylight.controller.sal.core.Node toADNode(final NodeId id) throws ConstructionException { + String aDNodeId = NodeMapping.toADNodeId(id); + return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, aDNodeId); + } + + public static NodeId toNodeId(final InstanceIdentifier id) { + final NodeKey key = id.firstKeyOf(Node.class, NodeKey.class); + Preconditions.checkArgument(key != null, "No node identifier found in %s", id); + return key.getId(); + } + + public static String toADNodeId(final NodeId nodeId) { + return nodeId.getValue(); + } + + public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorRef source) throws ConstructionException { + final InstanceIdentifier id = Preconditions.checkNotNull(source.getValue()); + final NodeConnectorKey key = id.firstKeyOf(NodeConnector.class, NodeConnectorKey.class); + return NodeMapping.toADNodeConnector(key.getId(), NodeMapping.toNodeId(id)); + } + + public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorId ncid, final NodeId nid) throws ConstructionException { + String nodeConnectorType = NodeMapping.toNodeConnectorType(ncid, nid); + Object aDNodeConnectorId = NodeMapping.toADNodeConnectorId(ncid, nid); + org.opendaylight.controller.sal.core.Node aDNode = NodeMapping.toADNode(nid); + return new org.opendaylight.controller.sal.core.NodeConnector(nodeConnectorType, aDNodeConnectorId, aDNode); + } + + public static String toNodeConnectorType(final NodeConnectorId ncId, final NodeId nodeId) { + if (ncId.equals(toLocalNodeConnectorId(nodeId))) { + return NodeConnectorIDType.SWSTACK; + } else if (ncId.equals(toNormalNodeConnectorId(nodeId))) { + return NodeConnectorIDType.HWPATH; + } else if (ncId.equals(toControllerNodeConnectorId(nodeId))) { + return NodeConnectorIDType.CONTROLLER; } - } else { - nodeConnectorId = new NodeConnectorId(Arguments.checkInstanceOf(nodeConnector.getID(), String.class)); + return MD_SAL_TYPE; } - final NodeConnectorKey connectorKey = new NodeConnectorKey(nodeConnectorId); - final InstanceIdentifier path = nodePath.child(NODECONNECTOR_CLASS, connectorKey); - return new NodeConnectorRef(path); - } - public static org.opendaylight.controller.sal.core.Node toADNode(final NodeRef node) throws ConstructionException { - return NodeMapping.toADNode(node.getValue()); - } + public static Object toADNodeConnectorId(final NodeConnectorId nodeConnectorId, final NodeId nodeId) { + if (nodeConnectorId.equals(toLocalNodeConnectorId(nodeId)) || + nodeConnectorId.equals(toNormalNodeConnectorId(nodeId)) || + nodeConnectorId.equals(toControllerNodeConnectorId(nodeId))) { + return org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID; + } + return nodeConnectorId.getValue(); + } - public static HashSet toADNodeConnectorProperties(final NodeConnectorUpdated nc) { - final FlowCapableNodeConnectorUpdated fcncu = nc.getAugmentation(FlowCapableNodeConnectorUpdated.class); - if (!Objects.equal(fcncu, null)) { - return NodeMapping.toADNodeConnectorProperties(fcncu); + public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) { + return new NodeConnectorId(node.getValue() + ":" + 4294967293L); } - return new HashSet(); - } - public static HashSet toADNodeConnectorProperties(final NodeConnector nc) { - final FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class); - if (!Objects.equal(fcnc, null)) { - return NodeMapping.toADNodeConnectorProperties(fcnc); + public static NodeConnectorId toLocalNodeConnectorId(final NodeId node) { + return new NodeConnectorId(node.getValue() + ":" + 4294967294L); } - return new HashSet(); - } - public static HashSet toADNodeConnectorProperties(final FlowNodeConnector fcncu) { + public static NodeConnectorId toNormalNodeConnectorId(final NodeId node) { + return new NodeConnectorId(node.getValue() + ":" + 4294967290L); + } + + public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) { + Preconditions.checkArgument(MD_SAL_TYPE.equals(node.getType())); + final String nodeId = Arguments.checkInstanceOf(node.getID(), String.class); + final NodeKey nodeKey = new NodeKey(new NodeId(nodeId)); + final InstanceIdentifier nodePath = InstanceIdentifier.builder(Nodes.class).child(NODE_CLASS, nodeKey).toInstance(); + return new NodeRef(nodePath); + } - final HashSet props = new HashSet<>(); - if (fcncu != null) { - if (fcncu.getCurrentFeature() != null && toAdBandwidth(fcncu.getCurrentFeature()) != null) { - props.add(toAdBandwidth(fcncu.getCurrentFeature())); + public static NodeConnectorRef toNodeConnectorRef(final org.opendaylight.controller.sal.core.NodeConnector nodeConnector) { + + final NodeRef node = NodeMapping.toNodeRef(nodeConnector.getNode()); + @SuppressWarnings("unchecked") + final InstanceIdentifier nodePath = ((InstanceIdentifier) node.getValue()); + NodeConnectorId nodeConnectorId = null; + + if (nodeConnector.getID().equals(org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID)) { + final NodeId nodeId = toNodeId(nodePath); + final String nodeConnectorType = nodeConnector.getType(); + if (nodeConnectorType.equals(NodeConnectorIDType.SWSTACK)) { + nodeConnectorId = toLocalNodeConnectorId(nodeId); + } else if (nodeConnectorType.equals(NodeConnectorIDType.HWPATH)) { + nodeConnectorId = toNormalNodeConnectorId(nodeId); + } else if (nodeConnectorType.equals(NodeConnectorIDType.CONTROLLER)) { + nodeConnectorId = toControllerNodeConnectorId(nodeId); + } + } else { + nodeConnectorId = new NodeConnectorId(Arguments.checkInstanceOf(nodeConnector.getID(), String.class)); } - if (fcncu.getAdvertisedFeatures() != null && toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()) != null) { - props.add(toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures())); + final NodeConnectorKey connectorKey = new NodeConnectorKey(nodeConnectorId); + final InstanceIdentifier path = nodePath.child(NODECONNECTOR_CLASS, connectorKey); + return new NodeConnectorRef(path); + } + + public static org.opendaylight.controller.sal.core.Node toADNode(final NodeRef node) throws ConstructionException { + return NodeMapping.toADNode(node.getValue()); + } + + public static HashSet toADNodeConnectorProperties(final NodeConnectorUpdated nc) { + final FlowCapableNodeConnectorUpdated fcncu = nc.getAugmentation(FlowCapableNodeConnectorUpdated.class); + if (!Objects.equal(fcncu, null)) { + return NodeMapping.toADNodeConnectorProperties(fcncu); } - if (fcncu.getSupported() != null && toAdSupportedBandwidth(fcncu.getSupported()) != null) { - props.add(toAdSupportedBandwidth(fcncu.getSupported())); + return new HashSet(); + } + + public static HashSet toADNodeConnectorProperties(final NodeConnector nc) { + final FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class); + if (!Objects.equal(fcnc, null)) { + return NodeMapping.toADNodeConnectorProperties(fcnc); } - if (fcncu.getPeerFeatures() != null && toAdPeerBandwidth(fcncu.getPeerFeatures()) != null) { - props.add(toAdPeerBandwidth(fcncu.getPeerFeatures())); + return new HashSet(); + } + + public static HashSet toADNodeConnectorProperties(final FlowNodeConnector fcncu) { + + final HashSet props = new HashSet<>(); + if (fcncu != null) { + if (fcncu.getCurrentFeature() != null && toAdBandwidth(fcncu.getCurrentFeature()) != null) { + props.add(toAdBandwidth(fcncu.getCurrentFeature())); + } + if (fcncu.getAdvertisedFeatures() != null && toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()) != null) { + props.add(toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures())); + } + if (fcncu.getSupported() != null && toAdSupportedBandwidth(fcncu.getSupported()) != null) { + props.add(toAdSupportedBandwidth(fcncu.getSupported())); + } + if (fcncu.getPeerFeatures() != null && toAdPeerBandwidth(fcncu.getPeerFeatures()) != null) { + props.add(toAdPeerBandwidth(fcncu.getPeerFeatures())); + } + if (fcncu.getName() != null && toAdName(fcncu.getName()) != null) { + props.add(toAdName(fcncu.getName())); + } + if (fcncu.getConfiguration() != null && toAdConfig(fcncu.getConfiguration()) != null) { + props.add(toAdConfig(fcncu.getConfiguration())); + } + if (fcncu.getState() != null && toAdState(fcncu.getState()) != null) { + props.add(toAdState(fcncu.getState())); + } } - if (fcncu.getName() != null && toAdName(fcncu.getName()) != null) { - props.add(toAdName(fcncu.getName())); + return props; + } + + public static Name toAdName(final String name) { + return new Name(name); + } + + public static Config toAdConfig(final PortConfig pc) { + Config config = null; + if (pc.isPORTDOWN()) { + config = new Config(Config.ADMIN_DOWN); + } else { + config = new Config(Config.ADMIN_UP); + } + return config; + } + + public static org.opendaylight.controller.sal.core.State toAdState(final State s) { + + org.opendaylight.controller.sal.core.State state = null; + if (s.isLinkDown()) { + state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_DOWN); + } else { + state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_UP); } - if (fcncu.getConfiguration() != null && toAdConfig(fcncu.getConfiguration()) != null) { - props.add(toAdConfig(fcncu.getConfiguration())); + return state; + } + + public static Bandwidth toAdBandwidth(final PortFeatures pf) { + Bandwidth bw = null; + if (pf.isTenMbHd() || pf.isTenMbFd()) { + bw = new Bandwidth(Bandwidth.BW10Mbps); + } else if (pf.isHundredMbHd() || pf.isHundredMbFd()) { + bw = new Bandwidth(Bandwidth.BW100Mbps); + } else if (pf.isOneGbHd() || pf.isOneGbFd()) { + bw = new Bandwidth(Bandwidth.BW1Gbps); + } else if (pf.isOneGbFd()) { + bw = new Bandwidth(Bandwidth.BW10Gbps); + } else if (pf.isTenGbFd()) { + bw = new Bandwidth(Bandwidth.BW10Gbps); + } else if (pf.isFortyGbFd()) { + bw = new Bandwidth(Bandwidth.BW40Gbps); + } else if (pf.isHundredGbFd()) { + bw = new Bandwidth(Bandwidth.BW100Gbps); + } else if (pf.isOneTbFd()) { + bw = new Bandwidth(Bandwidth.BW1Tbps); } - if (fcncu.getState() != null && toAdState(fcncu.getState()) != null) { - props.add(toAdState(fcncu.getState())); + return bw; + } + + public static AdvertisedBandwidth toAdAdvertizedBandwidth(final PortFeatures pf) { + AdvertisedBandwidth abw = null; + final Bandwidth bw = toAdBandwidth(pf); + if (bw != null) { + abw = new AdvertisedBandwidth(bw.getValue()); } + return abw; } - return props; - } - - public static Name toAdName(final String name) { - return new Name(name); - } - - public static Config toAdConfig(final PortConfig pc) { - Config config = null; - if (pc.isPORTDOWN()) { - config = new Config(Config.ADMIN_DOWN); - } else { - config = new Config(Config.ADMIN_UP); - } - return config; - } - - public static org.opendaylight.controller.sal.core.State toAdState(final State s) { - - org.opendaylight.controller.sal.core.State state = null; - if (s.isLinkDown()) { - state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_DOWN); - } else { - state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_UP); - } - return state; - } - - public static Bandwidth toAdBandwidth(final PortFeatures pf) { - Bandwidth bw = null; - if (pf.isTenMbHd() || pf.isTenMbFd()) { - bw = new Bandwidth(Bandwidth.BW10Mbps); - } else if (pf.isHundredMbHd() || pf.isHundredMbFd()) { - bw = new Bandwidth(Bandwidth.BW100Mbps); - } else if (pf.isOneGbHd() || pf.isOneGbFd()) { - bw = new Bandwidth(Bandwidth.BW1Gbps); - } else if (pf.isOneGbFd()) { - bw = new Bandwidth(Bandwidth.BW10Gbps); - } else if (pf.isTenGbFd()) { - bw = new Bandwidth(Bandwidth.BW10Gbps); - } else if (pf.isFortyGbFd()) { - bw = new Bandwidth(Bandwidth.BW40Gbps); - } else if (pf.isHundredGbFd()) { - bw = new Bandwidth(Bandwidth.BW100Gbps); - } else if (pf.isOneTbFd()) { - bw = new Bandwidth(Bandwidth.BW1Tbps); - } - return bw; - } - - public static AdvertisedBandwidth toAdAdvertizedBandwidth(final PortFeatures pf) { - AdvertisedBandwidth abw = null; - final Bandwidth bw = toAdBandwidth(pf); - if (bw != null) { - abw = new AdvertisedBandwidth(bw.getValue()); - } - return abw; - } - - public static SupportedBandwidth toAdSupportedBandwidth(final PortFeatures pf) { - SupportedBandwidth sbw = null; - final Bandwidth bw = toAdBandwidth(pf); - if (bw != null) { - sbw = new SupportedBandwidth(bw.getValue()); - } - return sbw; - } - - public static PeerBandwidth toAdPeerBandwidth(final PortFeatures pf) { - PeerBandwidth pbw = null; - final Bandwidth bw = toAdBandwidth(pf); - if (bw != null) { - pbw = new PeerBandwidth(bw.getValue()); - } - return pbw; - } - - public static HashSet toADNodeProperties(final NodeUpdated nu) { - final FlowCapableNodeUpdated fcnu = nu.getAugmentation(FlowCapableNodeUpdated.class); - if (fcnu != null) { - return toADNodeProperties(fcnu, nu.getId()); - } - return new HashSet(); - } - - public static HashSet toADNodeProperties(final FlowNode fcnu, final NodeId id) { - - final HashSet props = new HashSet<>(); - - if (fcnu != null) { - props.add(toADTimestamp()); - - // props.add(fcnu.supportedActions.toADActions) - TODO - if (id != null) { - props.add(toADMacAddress(id)); + + public static SupportedBandwidth toAdSupportedBandwidth(final PortFeatures pf) { + SupportedBandwidth sbw = null; + final Bandwidth bw = toAdBandwidth(pf); + if (bw != null) { + sbw = new SupportedBandwidth(bw.getValue()); } - SwitchFeatures switchFeatures = fcnu.getSwitchFeatures(); - if (switchFeatures != null) { - if (switchFeatures.getMaxTables() != null) { - props.add(toADTables(switchFeatures.getMaxTables())); - } - if (switchFeatures.getCapabilities() != null) { - props.add(toADCapabiliities(switchFeatures.getCapabilities())); + return sbw; + } + + public static PeerBandwidth toAdPeerBandwidth(final PortFeatures pf) { + PeerBandwidth pbw = null; + final Bandwidth bw = toAdBandwidth(pf); + if (bw != null) { + pbw = new PeerBandwidth(bw.getValue()); + } + return pbw; + } + + public static HashSet toADNodeProperties(final NodeUpdated nu) { + final FlowCapableNodeUpdated fcnu = nu.getAugmentation(FlowCapableNodeUpdated.class); + if (fcnu != null) { + return toADNodeProperties(fcnu, nu.getId()); + } + return new HashSet(); + } + + public static HashSet toADNodeProperties(final FlowNode fcnu, final NodeId id) { + + final HashSet props = new HashSet<>(); + + if (fcnu != null) { + props.add(toADTimestamp()); + + // props.add(fcnu.supportedActions.toADActions) - TODO + if (id != null) { + props.add(toADMacAddress(id)); } - if (switchFeatures.getMaxBuffers() != null) { - props.add(toADBuffers(switchFeatures.getMaxBuffers())); + SwitchFeatures switchFeatures = fcnu.getSwitchFeatures(); + if (switchFeatures != null) { + if (switchFeatures.getMaxTables() != null) { + props.add(toADTables(switchFeatures.getMaxTables())); + } + if (switchFeatures.getCapabilities() != null) { + props.add(toADCapabiliities(switchFeatures.getCapabilities())); + } + if (switchFeatures.getMaxBuffers() != null) { + props.add(toADBuffers(switchFeatures.getMaxBuffers())); + } } } + return props; } - return props; - } - - public static TimeStamp toADTimestamp() { - final Date date = new Date(); - final TimeStamp timestamp = new TimeStamp(date.getTime(), "connectedSince"); - return timestamp; - } - - public static MacAddress toADMacAddress(final NodeId id) { - final String nodeId = id.getValue().replaceAll("openflow:", ""); - BigInteger nodeIdRaw = new BigInteger(nodeId); - long lNodeId = nodeIdRaw.longValue(); - byte[] bytesFromDpid = ToSalConversionsUtils.bytesFromDpid(lNodeId); - return new MacAddress(bytesFromDpid); - } - - public static Tables toADTables(final Short tables) { - return new Tables(tables.byteValue()); - } - - public static Capabilities toADCapabiliities(final List> capabilities) { - - int b = 0; - for (Class capability : capabilities) { - if (capability.equals(FlowFeatureCapabilityFlowStats.class)) { - b = Capabilities.CapabilitiesType.FLOW_STATS_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityTableStats.class)) { - b = Capabilities.CapabilitiesType.TABLE_STATS_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityPortStats.class)) { - b = Capabilities.CapabilitiesType.PORT_STATS_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityStp.class)) { - b = Capabilities.CapabilitiesType.STP_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityIpReasm.class)) { - b = Capabilities.CapabilitiesType.IP_REASSEM_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityQueueStats.class)) { - b = Capabilities.CapabilitiesType.QUEUE_STATS_CAPABILITY.getValue() | b; - } else if (capability.equals(FlowFeatureCapabilityArpMatchIp.class)) { - b = Capabilities.CapabilitiesType.ARP_MATCH_IP_CAPABILITY.getValue() | b; + + public static TimeStamp toADTimestamp() { + final Date date = new Date(); + final TimeStamp timestamp = new TimeStamp(date.getTime(), "connectedSince"); + return timestamp; + } + + public static MacAddress toADMacAddress(final NodeId id) { + final String nodeId = id.getValue().replaceAll("openflow:", ""); + BigInteger nodeIdRaw = new BigInteger(nodeId); + long lNodeId = nodeIdRaw.longValue(); + byte[] bytesFromDpid = ToSalConversionsUtils.bytesFromDpid(lNodeId); + return new MacAddress(bytesFromDpid); + } + + public static Tables toADTables(final Short tables) { + return new Tables(tables.byteValue()); + } + + public static Capabilities toADCapabiliities(final List> capabilities) { + + int b = 0; + for (Class capability : capabilities) { + if (capability.equals(FlowFeatureCapabilityFlowStats.class)) { + b = Capabilities.CapabilitiesType.FLOW_STATS_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityTableStats.class)) { + b = Capabilities.CapabilitiesType.TABLE_STATS_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityPortStats.class)) { + b = Capabilities.CapabilitiesType.PORT_STATS_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityStp.class)) { + b = Capabilities.CapabilitiesType.STP_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityIpReasm.class)) { + b = Capabilities.CapabilitiesType.IP_REASSEM_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityQueueStats.class)) { + b = Capabilities.CapabilitiesType.QUEUE_STATS_CAPABILITY.getValue() | b; + } else if (capability.equals(FlowFeatureCapabilityArpMatchIp.class)) { + b = Capabilities.CapabilitiesType.ARP_MATCH_IP_CAPABILITY.getValue() | b; + } } + return new Capabilities(b); } - return new Capabilities(b); - } - public static Buffers toADBuffers(final Long buffers) { - return new Buffers(buffers.intValue()); - } + public static Buffers toADBuffers(final Long buffers) { + return new Buffers(buffers.intValue()); + } } diff --git a/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-match-types.yang b/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-match-types.yang index b02b0dc25c..efe1ce3e3a 100644 --- a/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-match-types.yang +++ b/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-match-types.yang @@ -269,6 +269,12 @@ module opendaylight-match-types { } } + grouping "tcp-flag-match-fields" { + leaf tcp-flag { + type uint16; + } + } + grouping match { leaf in-port { type inv:node-connector-id; @@ -340,5 +346,9 @@ module opendaylight-match-types { container "protocol-match-fields" { uses "protocol-match-fields"; } + + container tcp-flag-match { + uses "tcp-flag-match-fields"; + } } } \ No newline at end of file diff --git a/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-table-types.yang b/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-table-types.yang index e74b548342..c271f8f4d0 100644 --- a/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-table-types.yang +++ b/opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-table-types.yang @@ -188,6 +188,10 @@ module opendaylight-table-types { base match-field; description "Match for IPv6 Extension Header pseudo-field"; } + identity tcp_flag { + base match-field; + description "TCP Flag Match"; + } grouping set-field-match { list set-field-match { diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index 94bd0731aa..9222734360 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -143,12 +143,32 @@ + org.apache.maven.plugins maven-checkstyle-plugin - ${checkstyle.version} + 2.12 + + false + false + checkstyle-logging.xml + true + true + ${project.basedir} + **\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang + **\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/ + + + + org.opendaylight.yangtools + checkstyle-logging + ${yangtools.version} + + - none + + check + diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/BrokerImplClassLoader.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/BrokerImplClassLoader.java index cab4fe90e0..fdd9350680 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/BrokerImplClassLoader.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/BrokerImplClassLoader.java @@ -6,25 +6,22 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.controller.sal.binding.codegen.impl; -import org.eclipse.xtext.xbase.lib.Exceptions; - -@SuppressWarnings("all") public class BrokerImplClassLoader extends ClassLoader { - private final ClassLoader spiClassLoader; + private final ClassLoader spiClassLoader; - public BrokerImplClassLoader(final ClassLoader model, final ClassLoader spi) { - super(model); - this.spiClassLoader = spi; - } + public BrokerImplClassLoader(final ClassLoader model, final ClassLoader spi) { + super(model); + this.spiClassLoader = spi; + } - public Class loadClass(final String name) throws ClassNotFoundException { - try { - return super.loadClass(name); - } catch (ClassNotFoundException e) { - return this.spiClassLoader.loadClass(name); + @Override + public Class loadClass(final String name) throws ClassNotFoundException { + try { + return super.loadClass(name); + } catch (ClassNotFoundException e) { + return this.spiClassLoader.loadClass(name); + } } - } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java index 8276446766..1ec4aa2d30 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java @@ -11,6 +11,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; @@ -24,6 +25,7 @@ import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.ForwardingBlockingQueue; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -62,41 +64,49 @@ public class SingletonHolder { try { queueSize = Integer.parseInt(queueValue); logger.trace("Queue size was set to {}", queueSize); - }catch(NumberFormatException e) { + } catch (NumberFormatException e) { logger.warn("Cannot parse {} as set by {}, using default {}", queueValue, NOTIFICATION_QUEUE_SIZE_PROPERTY, queueSize); } } + // Overriding the queue: // ThreadPoolExecutor would not create new threads if the queue is not full, thus adding // occurs in RejectedExecutionHandler. // This impl saturates threadpool first, then queue. When both are full caller will get blocked. - BlockingQueue queue = new LinkedBlockingQueue(queueSize) { - private static final long serialVersionUID = 1L; + final BlockingQueue delegate = new LinkedBlockingQueue<>(queueSize); + final BlockingQueue queue = new ForwardingBlockingQueue() { + @Override + protected BlockingQueue delegate() { + return delegate; + } @Override - public boolean offer(Runnable r) { - // ThreadPoolExecutor will spawn a new thread after core size is reached only if the queue.offer returns false. + public boolean offer(final Runnable r) { + // ThreadPoolExecutor will spawn a new thread after core size is reached only + // if the queue.offer returns false. return false; } }; - ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-notification-%d").build(); + final ThreadFactory factory = new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("md-sal-binding-notification-%d") + .build(); - ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS, - NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue , factory, + final ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS, + NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue, factory, new RejectedExecutionHandler() { - // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue. - @Override - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - try { - executor.getQueue().put(r); - } catch (InterruptedException e) { - Thread.currentThread().interrupt();// set interrupt flag after clearing - throw new IllegalStateException(e); - } - } - }); + // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue. + @Override + public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { + try { + executor.getQueue().put(r); + } catch (InterruptedException e) { + throw new RejectedExecutionException("Interrupted while waiting on the queue", e); + } + } + }); NOTIFICATION_EXECUTOR = MoreExecutors.listeningDecorator(executor); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java new file mode 100644 index 0000000000..5e7c91374f --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.binding.impl; + +import org.opendaylight.controller.sal.binding.api.NotificationListener; +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; +import org.opendaylight.yangtools.yang.binding.Notification; + +import com.google.common.base.Preconditions; + +/** + * Abstract implementation of {@link NotificationListenerRegistration}. + * + * @param Notification type + */ +abstract class AbstractNotificationListenerRegistration extends AbstractListenerRegistration> implements NotificationListenerRegistration { + private final Class type; + + protected AbstractNotificationListenerRegistration(final Class type, final NotificationListener listener) { + super(listener); + this.type = Preconditions.checkNotNull(type); + } + + @Override + public Class getType() { + return type; + } + + @Override + @SuppressWarnings("unchecked") + public void notify(final Notification notification) { + if (!isClosed()) { + getInstance().onNotification((T)notification); + } + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java new file mode 100644 index 0000000000..f0db891f14 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.binding.impl; + +import org.opendaylight.controller.sal.binding.api.NotificationListener; +import org.opendaylight.yangtools.yang.binding.Notification; + +import com.google.common.base.Preconditions; + +/** + * An aggregated listener registration. This is a result of registering an invoker which can handle multiple + * interfaces at the same time. In order to support correct delivery, we need to maintain per-type registrations + * which get squashed if a notification which implements multiple interfaces is encountered. + * + * We take care of that by implementing alternate {@link #hashCode()}/{@link #equals(Object)}, which resolve + * to the backing aggregator. + * + * @param Notification type + * @param Aggregator type + */ +abstract class AggregatedNotificationListenerRegistration extends AbstractNotificationListenerRegistration { + private final A aggregator; + + protected AggregatedNotificationListenerRegistration(final Class type, final NotificationListener listener, final A aggregator) { + super(type, listener); + this.aggregator = Preconditions.checkNotNull(aggregator); + } + + protected A getAggregator() { + return aggregator; + } + + @Override + public int hashCode() { + return aggregator.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!this.getClass().equals(obj.getClass())) { + return false; + } + + return aggregator.equals(((AggregatedNotificationListenerRegistration)obj).aggregator); + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java new file mode 100644 index 0000000000..4d893aa7be --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.binding.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.opendaylight.yangtools.yang.binding.Notification; + +import com.google.common.base.Predicate; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +/** + * An immutable view of the current generation of listeners. + */ +final class ListenerMapGeneration { + private static final int CACHE_MAX_ENTRIES = 1000; + + /** + * Constant map of notification type to subscribed listeners. + */ + private final Multimap, NotificationListenerRegistration> typeToListeners; + + /** + * Dynamic cache of notification implementation to matching listeners. This cache loads entries based on + * the contents of the {@link #typeToListeners} map. + */ + private final LoadingCache, Iterable>> implementationToListeners = + CacheBuilder.newBuilder() + .weakKeys() + .maximumSize(CACHE_MAX_ENTRIES) + .build(new CacheLoader, Iterable>>() { + @Override + public Iterable> load(final Class key) { + final Set> regs = new HashSet<>(); + + for (final Class type : getNotificationTypes(key)) { + @SuppressWarnings("unchecked") + final Collection> l = typeToListeners.get((Class) type); + if (l != null) { + regs.addAll(l); + } + } + + return ImmutableSet.copyOf(regs); + } + }); + + ListenerMapGeneration() { + typeToListeners = ImmutableMultimap.of(); + } + + ListenerMapGeneration(final Multimap, NotificationListenerRegistration> listeners) { + this.typeToListeners = ImmutableMultimap.copyOf(listeners); + } + + /** + * Current listeners. Exposed for creating the next generation. + * + * @return Current type-to-listener map. + */ + Multimap, NotificationListenerRegistration> getListeners() { + return typeToListeners; + } + + /** + * Look up the listeners which need to see this notification delivered. + * + * @param notification Notification object + * @return Iterable of listeners, guaranteed to be nonnull. + */ + public Iterable> listenersFor(final Notification notification) { + // Safe to use, as our loader does not throw checked exceptions + return implementationToListeners.getUnchecked(notification.getClass()); + } + + public Iterable> getKnownTypes() { + return typeToListeners.keySet(); + } + + private static Iterable> getNotificationTypes(final Class cls) { + final Class[] ifaces = cls.getInterfaces(); + return Iterables.filter(Arrays.asList(ifaces), new Predicate>() { + @Override + public boolean apply(final Class input) { + if (Notification.class.equals(input)) { + return false; + } + return Notification.class.isAssignableFrom(input); + } + }); + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java index b999a6f01c..df09f78620 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java @@ -11,10 +11,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance; -import org.opendaylight.yangtools.concepts.util.ListenerRegistry; import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance; import org.opendaylight.controller.sal.binding.api.mount.MountProviderService; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.util.ListenerRegistry; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +27,7 @@ public class MountPointManagerImpl implements MountProviderService { private final ConcurrentMap, BindingMountPointImpl> mountPoints; private final ListenerRegistry listeners = ListenerRegistry.create(); - + private ListeningExecutorService notificationExecutor; private ListeningExecutorService dataCommitExecutor; @@ -39,7 +39,7 @@ public class MountPointManagerImpl implements MountProviderService { return notificationExecutor; } - public void setNotificationExecutor(ListeningExecutorService notificationExecutor) { + public void setNotificationExecutor(final ListeningExecutorService notificationExecutor) { this.notificationExecutor = notificationExecutor; } @@ -47,12 +47,12 @@ public class MountPointManagerImpl implements MountProviderService { return dataCommitExecutor; } - public void setDataCommitExecutor(ListeningExecutorService dataCommitExecutor) { + public void setDataCommitExecutor(final ListeningExecutorService dataCommitExecutor) { this.dataCommitExecutor = dataCommitExecutor; } @Override - public synchronized BindingMountPointImpl createMountPoint(InstanceIdentifier path) { + public synchronized BindingMountPointImpl createMountPoint(final InstanceIdentifier path) { BindingMountPointImpl potential = mountPoints.get(path); if (potential != null) { throw new IllegalStateException("Mount point already exists."); @@ -61,7 +61,7 @@ public class MountPointManagerImpl implements MountProviderService { } @Override - public BindingMountPointImpl createOrGetMountPoint(InstanceIdentifier path) { + public BindingMountPointImpl createOrGetMountPoint(final InstanceIdentifier path) { BindingMountPointImpl potential = getMountPoint(path); if (potential != null) { return potential; @@ -70,18 +70,17 @@ public class MountPointManagerImpl implements MountProviderService { } @Override - public BindingMountPointImpl getMountPoint(InstanceIdentifier path) { + public BindingMountPointImpl getMountPoint(final InstanceIdentifier path) { return mountPoints.get(path); } - private synchronized BindingMountPointImpl createOrGetMountPointImpl(InstanceIdentifier path) { + private synchronized BindingMountPointImpl createOrGetMountPointImpl(final InstanceIdentifier path) { BindingMountPointImpl potential = getMountPoint(path); if (potential != null) { return potential; } RpcProviderRegistryImpl rpcRegistry = new RpcProviderRegistryImpl("mount"); - NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl(); - notificationBroker.setExecutor(getNotificationExecutor()); + NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl(getNotificationExecutor()); DataBrokerImpl dataBroker = new DataBrokerImpl(); dataBroker.setExecutor(getDataCommitExecutor()); BindingMountPointImpl mountInstance = new BindingMountPointImpl(path, rpcRegistry, notificationBroker, @@ -91,7 +90,7 @@ public class MountPointManagerImpl implements MountProviderService { return mountInstance; } - private void notifyMountPointCreated(InstanceIdentifier path) { + private void notifyMountPointCreated(final InstanceIdentifier path) { for (ListenerRegistration listener : listeners) { try { listener.getInstance().onMountPointCreated(path); @@ -102,28 +101,28 @@ public class MountPointManagerImpl implements MountProviderService { } @Override - public ListenerRegistration registerProvisionListener(MountProvisionListener listener) { + public ListenerRegistration registerProvisionListener(final MountProvisionListener listener) { return listeners.register(listener); } public class BindingMountPointImpl extends - AbstractBindingSalProviderInstance + AbstractBindingSalProviderInstance implements MountProviderInstance { - private InstanceIdentifier identifier; + private final InstanceIdentifier identifier; - public BindingMountPointImpl(org.opendaylight.yangtools.yang.binding.InstanceIdentifier identifier, - RpcProviderRegistryImpl rpcRegistry, NotificationBrokerImpl notificationBroker, - DataBrokerImpl dataBroker) { + public BindingMountPointImpl(final org.opendaylight.yangtools.yang.binding.InstanceIdentifier identifier, + final RpcProviderRegistryImpl rpcRegistry, final NotificationBrokerImpl notificationBroker, + final DataBrokerImpl dataBroker) { super(rpcRegistry, notificationBroker, dataBroker); this.identifier = identifier; } // Needed only for BI Connector public DataBrokerImpl getDataBrokerImpl() { - return (DataBrokerImpl) getDataBroker(); + return getDataBroker(); } - + @Override public InstanceIdentifier getIdentifier() { return this.identifier; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java new file mode 100644 index 0000000000..258ba51777 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2013 Cisco Systems, 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.controller.sal.binding.impl; + +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.concurrent.GuardedBy; + +import org.opendaylight.controller.sal.binding.api.NotificationListener; +import org.opendaylight.controller.sal.binding.api.NotificationProviderService; +import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; +import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker; +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.util.ListenerRegistry; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +public class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(NotificationBrokerImpl.class); + + private final ListenerRegistry interestListeners = + ListenerRegistry.create(); + private final AtomicReference listeners = new AtomicReference<>(new ListenerMapGeneration()); + private final ExecutorService executor; + + public NotificationBrokerImpl(final ExecutorService executor) { + this.executor = Preconditions.checkNotNull(executor); + } + + @Override + public void publish(final Notification notification) { + publish(notification, executor); + } + + @Override + public void publish(final Notification notification, final ExecutorService service) { + for (NotificationListenerRegistration r : listeners.get().listenersFor(notification)) { + service.submit(new NotifyTask(r, notification)); + } + } + + @GuardedBy("this") + private Multimap, NotificationListenerRegistration> mutableListeners() { + return HashMultimap.create(listeners.get().getListeners()); + } + + private final void addRegistrations(final NotificationListenerRegistration... registrations) { + synchronized (this) { + final Multimap, NotificationListenerRegistration> newListeners = + mutableListeners(); + for (NotificationListenerRegistration reg : registrations) { + newListeners.put(reg.getType(), reg); + } + + listeners.set(new ListenerMapGeneration(newListeners)); + } + + // Notifications are dispatched out of lock... + for (NotificationListenerRegistration reg : registrations) { + announceNotificationSubscription(reg.getType()); + } + } + + private synchronized void removeRegistrations(final NotificationListenerRegistration... registrations) { + final Multimap, NotificationListenerRegistration> newListeners = + mutableListeners(); + + for (NotificationListenerRegistration reg : registrations) { + newListeners.remove(reg.getType(), reg); + } + + listeners.set(new ListenerMapGeneration(newListeners)); + } + + private void announceNotificationSubscription(final Class notification) { + for (final ListenerRegistration listener : interestListeners) { + try { + listener.getInstance().onNotificationSubscribtion(notification); + } catch (Exception e) { + LOG.warn("Listener {} reported unexpected error on notification {}", + listener.getInstance(), notification, e); + } + } + } + + @Override + public ListenerRegistration registerInterestListener(final NotificationInterestListener interestListener) { + final ListenerRegistration registration = this.interestListeners.register(interestListener); + + for (final Class notification : listeners.get().getKnownTypes()) { + interestListener.onNotificationSubscribtion(notification); + } + return registration; + } + + @Override + public NotificationListenerRegistration registerNotificationListener(final Class notificationType, final NotificationListener listener) { + final NotificationListenerRegistration reg = new AbstractNotificationListenerRegistration(notificationType, listener) { + @Override + protected void removeRegistration() { + removeRegistrations(this); + } + }; + + addRegistrations(reg); + return reg; + } + + @Override + public ListenerRegistration registerNotificationListener(final org.opendaylight.yangtools.yang.binding.NotificationListener listener) { + final NotificationInvoker invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener); + final Set> types = invoker.getSupportedNotifications(); + final NotificationListenerRegistration[] regs = new NotificationListenerRegistration[types.size()]; + + // Populate the registrations... + int i = 0; + for (Class type : types) { + regs[i] = new AggregatedNotificationListenerRegistration(type, invoker.getInvocationProxy(), regs) { + @Override + protected void removeRegistration() { + // Nothing to do, will be cleaned up by parent (below) + } + }; + ++i; + } + + // ... now put them to use ... + addRegistrations(regs); + + // ... finally return the parent registration + return new AbstractListenerRegistration(listener) { + @Override + protected void removeRegistration() { + removeRegistrations(regs); + for (ListenerRegistration reg : regs) { + reg.close(); + } + } + }; + } + + @Override + public void close() { + } + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend deleted file mode 100644 index 6d675b4b5e..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.sal.binding.impl - -import com.google.common.collect.HashMultimap -import com.google.common.collect.ImmutableSet -import com.google.common.collect.Multimap -import com.google.common.collect.Multimaps -import java.util.Collections -import java.util.concurrent.Callable -import java.util.concurrent.ExecutorService -import java.util.concurrent.Future -import java.util.Set -import org.opendaylight.controller.sal.binding.api.NotificationListener -import org.opendaylight.controller.sal.binding.api.NotificationProviderService -import org.opendaylight.controller.sal.binding.api.NotificationProviderService.NotificationInterestListener -import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder -import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration -import org.opendaylight.yangtools.concepts.ListenerRegistration -import org.opendaylight.yangtools.concepts.Registration -import org.opendaylight.yangtools.concepts.util.ListenerRegistry -import org.opendaylight.yangtools.yang.binding.Notification -import org.slf4j.LoggerFactory - -class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable { - - val ListenerRegistry interestListeners = ListenerRegistry.create; - - val Multimap, NotificationListener> listeners; - - @Property - var ExecutorService executor; - - val logger = LoggerFactory.getLogger(NotificationBrokerImpl) - - new() { - listeners = Multimaps.synchronizedSetMultimap(HashMultimap.create()) - } - - @Deprecated - new(ExecutorService executor) { - listeners = Multimaps.synchronizedSetMultimap(HashMultimap.create()) - this.executor = executor; - } - - def getNotificationTypes(Notification notification) { - notification.class.interfaces.filter[it != Notification && Notification.isAssignableFrom(it)] - } - - override publish(Notification notification) { - publish(notification, executor) - } - - override publish(Notification notification, ExecutorService service) { - val allTypes = notification.notificationTypes - - var Iterable> listenerToNotify = Collections.emptySet(); - for (type : allTypes) { - listenerToNotify = listenerToNotify + listeners.get(type as Class) - } - val tasks = listenerToNotify.map[new NotifyTask(it, notification)].toSet; - submitAll(executor,tasks); - } - - def submitAll(ExecutorService service, Set tasks) { - val ret = ImmutableSet.>builder(); - for(task : tasks) { - ret.add(service.submit(task)); - } - return ret.build(); - } - - override registerNotificationListener(Class notificationType, - NotificationListener listener) { - val reg = new GenericNotificationRegistration(notificationType, listener, this); - listeners.put(notificationType, listener); - announceNotificationSubscription(notificationType); - return reg; - } - - def announceNotificationSubscription(Class notification) { - for (listener : interestListeners) { - try { - listener.instance.onNotificationSubscribtion(notification); - } catch (Exception e) { - logger.error("", e.message) - } - } - } - - override registerNotificationListener( - org.opendaylight.yangtools.yang.binding.NotificationListener listener) { - val invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener); - for (notifyType : invoker.supportedNotifications) { - listeners.put(notifyType, invoker.invocationProxy) - announceNotificationSubscription(notifyType) - } - val registration = new GeneratedListenerRegistration(listener, invoker,this); - return registration as Registration; - } - - protected def unregisterListener(GenericNotificationRegistration reg) { - listeners.remove(reg.type, reg.instance); - } - - protected def unregisterListener(GeneratedListenerRegistration reg) { - for (notifyType : reg.invoker.supportedNotifications) { - listeners.remove(notifyType, reg.invoker.invocationProxy) - } - } - - override close() { - //FIXME: implement properly. - } - - override registerInterestListener(NotificationInterestListener interestListener) { - val registration = interestListeners.register(interestListener); - - for(notification : listeners.keySet) { - interestListener.onNotificationSubscribtion(notification); - } - return registration - } -} - -class GenericNotificationRegistration extends AbstractObjectRegistration> implements ListenerRegistration> { - - @Property - val Class type; - - var NotificationBrokerImpl notificationBroker; - - public new(Class type, NotificationListener instance, NotificationBrokerImpl broker) { - super(instance); - _type = type; - notificationBroker = broker; - } - - override protected removeRegistration() { - notificationBroker.unregisterListener(this); - notificationBroker = null; - } -} - -class GeneratedListenerRegistration extends AbstractObjectRegistration implements ListenerRegistration { - - @Property - val NotificationInvoker invoker; - - var NotificationBrokerImpl notificationBroker; - - - new(org.opendaylight.yangtools.yang.binding.NotificationListener instance, NotificationInvoker invoker, NotificationBrokerImpl broker) { - super(instance); - _invoker = invoker; - notificationBroker = broker; - } - - override protected removeRegistration() { - notificationBroker.unregisterListener(this); - notificationBroker = null; - invoker.close(); - } -} - -@Data -class NotifyTask implements Callable { - - private static val log = LoggerFactory.getLogger(NotifyTask); - - @SuppressWarnings("rawtypes") - val NotificationListener listener; - val Notification notification; - - override call() { - //Only logging the complete notification in debug mode - try { - if(log.isDebugEnabled){ - log.debug("Delivering notification {} to {}",notification,listener); - } else { - log.trace("Delivering notification {} to {}",notification.class.name,listener); - } - listener.onNotification(notification); - if(log.isDebugEnabled){ - log.debug("Notification delivered {} to {}",notification,listener); - } else { - log.trace("Notification delivered {} to {}",notification.class.name,listener); - } - } catch (Exception e) { - log.error("Unhandled exception thrown by listener: {}", listener, e); - } - return null; - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java new file mode 100644 index 0000000000..3dba868c6f --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.binding.impl; + +import org.opendaylight.controller.sal.binding.api.NotificationListener; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.Notification; + +/** + * A registration of a {@link NotificationListener}. Allows query of the type + * of the notification and dispatching the notification atomically with regard + * to unregistration. + * + * @param Type of notification + */ +interface NotificationListenerRegistration extends ListenerRegistration> { + /** + * Return the interface class of the notification type. + * + * @return Notification type. + */ + Class getType(); + + /** + * Dispatch a notification to the listener. + * + * @param notification Notification to be dispatched + */ + void notify(Notification notification); +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java new file mode 100644 index 0000000000..2622a71e55 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2013 Cisco Systems, 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.controller.sal.binding.impl; + +import org.opendaylight.yangtools.yang.binding.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; + +class NotifyTask implements Runnable { + private static final Logger LOG = LoggerFactory.getLogger(NotifyTask.class); + + private final NotificationListenerRegistration registration; + private final Notification notification; + + public NotifyTask(final NotificationListenerRegistration registration, final Notification notification) { + this.registration = Preconditions.checkNotNull(registration); + this.notification = Preconditions.checkNotNull(notification); + } + + @SuppressWarnings("unchecked") + private NotificationListenerRegistration getRegistration() { + return (NotificationListenerRegistration)registration; + } + + @Override + public void run() { + if (LOG.isDebugEnabled()) { + LOG.debug("Delivering notification {} to {}", notification, registration.getInstance()); + } else { + LOG.trace("Delivering notification {} to {}", notification.getClass().getName(), registration.getInstance()); + } + + try { + getRegistration().notify(notification); + } catch (final Exception e) { + LOG.error("Unhandled exception thrown by listener: {}", registration.getInstance(), e); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Notification delivered {} to {}", notification, registration.getInstance()); + } else { + LOG.trace("Notification delivered {} to {}", notification.getClass().getName(), registration.getInstance()); + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((registration== null) ? 0 : registration.hashCode()); + result = prime * result + ((notification== null) ? 0 : notification.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NotifyTask other = (NotifyTask) obj; + if (registration == null) { + if (other.registration != null) + return false; + } else if (!registration.equals(other.registration)) + return false; + if (notification == null) { + if (other.notification != null) + return false; + } else if (!notification.equals(other.notification)) + return false; + return true; + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("listener", registration) + .add("notification", notification.getClass()) + .toString(); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java index 53423f6b09..ba9b2b7f55 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java @@ -103,7 +103,7 @@ AbstractDataReadRouter { "Only one simple node for key $s is allowed in node $s", keyValue.getKey(), node); checkState( - simpleNode.get(0).getValue() == keyValue.getValue(), + simpleNode.get(0).getValue().equals(keyValue.getValue()), "Key node must equal to instance identifier value in node $s", node); ret.put(keyValue.getKey(), simpleNode.get(0)); diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index 9ad95c18d9..0dd25292c2 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -51,6 +51,10 @@ org.opendaylight.controller sal-binding-api + + org.opendaylight.controller + sal-binding-config + org.opendaylight.controller threadpool-config-api @@ -142,11 +146,6 @@ junit test - - org.mockito - mockito-all - test - org.opendaylight.controller logback-config @@ -169,6 +168,11 @@ jar test + + org.opendaylight.yangtools + mockito-configuration + test + diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 1839de1597..f73d9cc72f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -10,38 +10,29 @@ package org.opendaylight.controller.config.yang.md.sal.connector.netconf; import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkCondition; import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull; -import com.google.common.net.InetAddresses; -import io.netty.util.HashedWheelTimer; -import io.netty.util.concurrent.GlobalEventExecutor; import java.io.File; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.URI; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; + import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; -import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; -import org.opendaylight.controller.sal.binding.api.data.DataProviderService; -import org.opendaylight.controller.sal.connect.netconf.InventoryUtils; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.controller.sal.connect.netconf.NetconfDevice; -import org.opendaylight.controller.sal.connect.netconf.NetconfDeviceListener; -import org.opendaylight.controller.sal.core.api.data.DataChangeListener; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.protocol.framework.ReconnectStrategy; import org.opendaylight.protocol.framework.ReconnectStrategyFactory; import org.opendaylight.protocol.framework.TimedReconnectStrategy; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider; import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider; import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; @@ -51,6 +42,10 @@ import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Preconditions; +import com.google.common.net.InetAddresses; +import io.netty.util.HashedWheelTimer; + /** * */ @@ -58,22 +53,20 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co { private static final Logger logger = LoggerFactory.getLogger(NetconfConnectorModule.class); - private static ExecutorService GLOBAL_PROCESSING_EXECUTOR = null; private static AbstractCachingSchemaSourceProvider GLOBAL_NETCONF_SOURCE_PROVIDER = null; private BundleContext bundleContext; - public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, NetconfConnectorModule oldModule, java.lang.AutoCloseable oldInstance) { + public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final NetconfConnectorModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @Override protected void customValidation() { checkNotNull(getAddress(), addressJmxAttribute); - //checkState(getAddress().getIpv4Address() != null || getAddress().getIpv6Address() != null,"Address must be set."); checkNotNull(getPort(), portJmxAttribute); checkNotNull(getDomRegistry(), portJmxAttribute); checkNotNull(getDomRegistry(), domRegistryJmxAttribute); @@ -96,41 +89,83 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co checkNotNull(getPassword(), passwordJmxAttribute); } + // FIXME BUG 944 remove this warning + if(getBindingRegistry() == null) { + logger.warn("Configuration property: \"binding-registry\" not set for sal-netconf-connector (" + getIdentifier() + "). " + + "Netconf-connector now requires a dependency on \"binding-broker-osgi-registry\". " + + "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " + + "Please set the property as in \"01-netconf-connector\" initial config file. " + + "The service will be retrieved from OSGi service registry now."); + } + + // FIXME BUG 944 remove this warning + if(getProcessingExecutor() == null) { + logger.warn("Configuration property: \"processing-executor\" not set for sal-netconf-connector (" + getIdentifier() + "). " + + "Netconf-connector now requires a dependency on \"threadpool\". " + + "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " + + "Please set the property as in \"01-netconf-connector\" initial config file. " + + "New instance will be created for the executor."); + } } @Override public java.lang.AutoCloseable createInstance() { - ServiceReference serviceReference = bundleContext.getServiceReference(DataProviderService.class); + final RemoteDeviceId id = new RemoteDeviceId(getIdentifier()); - DataProviderService dataProviderService = - bundleContext.getService(serviceReference); + final ExecutorService globalProcessingExecutor = getGlobalProcessingExecutor(); - getDomRegistryDependency(); - NetconfDevice device = new NetconfDevice(getIdentifier().getInstanceName()); + final Broker domBroker = getDomRegistryDependency(); + final BindingAwareBroker bindingBroker = getBindingRegistryBackwards(); - device.setClientConfig(getClientConfig(device)); + final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor); + final NetconfDevice device = + NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade); + final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device); + final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener); - device.setProcessingExecutor(getGlobalProcessingExecutor()); + // FIXME BUG-944 remove backwards compatibility + final NetconfClientDispatcher dispatcher = getClientDispatcher() == null ? createDispatcher() : getClientDispatcherDependency(); + listener.initializeRemoteConnection(dispatcher, clientConfig); - device.setEventExecutor(getEventExecutorDependency()); - device.setDispatcher(getClientDispatcher() == null ? createDispatcher() : getClientDispatcherDependency()); - device.setSchemaSourceProvider(getGlobalNetconfSchemaProvider(bundleContext)); - device.setDataProviderService(dataProviderService); - getDomRegistryDependency().registerProvider(device, bundleContext); - device.start(); - return device; + return new AutoCloseable() { + @Override + public void close() throws Exception { + listener.close(); + salFacade.close(); + } + }; + } + + private BindingAwareBroker getBindingRegistryBackwards() { + if(getBindingRegistry() != null) { + return getBindingRegistryDependency(); + } else { + // FIXME BUG 944 remove backwards compatibility + final ServiceReference serviceReference = bundleContext.getServiceReference(BindingAwareBroker.class); + Preconditions + .checkNotNull( + serviceReference, + "Unable to retrieve %s from OSGi service registry, use binding-registry config property to inject %s with config subsystem", + BindingAwareBroker.class, BindingAwareBroker.class); + return bundleContext.getService(serviceReference); + } } private ExecutorService getGlobalProcessingExecutor() { - return GLOBAL_PROCESSING_EXECUTOR == null ? Executors.newCachedThreadPool() : GLOBAL_PROCESSING_EXECUTOR; + if(getProcessingExecutor() != null) { + return getProcessingExecutorDependency().getExecutor(); + } else { + // FIXME BUG 944 remove backwards compatibility + return Executors.newCachedThreadPool(); + } } - private synchronized AbstractCachingSchemaSourceProvider getGlobalNetconfSchemaProvider(BundleContext bundleContext) { + private synchronized AbstractCachingSchemaSourceProvider getGlobalNetconfSchemaProvider() { if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) { - String storageFile = "cache/schema"; + final String storageFile = "cache/schema"; // File directory = bundleContext.getDataFile(storageFile); - File directory = new File(storageFile); - SchemaSourceProvider defaultProvider = SchemaSourceProviders.noopProvider(); + final File directory = new File(storageFile); + final SchemaSourceProvider defaultProvider = SchemaSourceProviders.noopProvider(); GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory); } return GLOBAL_NETCONF_SOURCE_PROVIDER; @@ -146,20 +181,20 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co return new NetconfClientDispatcherImpl(getBossThreadGroupDependency(), getWorkerThreadGroupDependency(), new HashedWheelTimer()); } - public void setBundleContext(BundleContext bundleContext) { + public void setBundleContext(final BundleContext bundleContext) { this.bundleContext = bundleContext; } - public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDevice device) { - InetSocketAddress socketAddress = getSocketAddress(); - ReconnectStrategy strategy = getReconnectStrategy(); - long clientConnectionTimeoutMillis = getConnectionTimeoutMillis(); + public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDeviceCommunicator listener) { + final InetSocketAddress socketAddress = getSocketAddress(); + final ReconnectStrategy strategy = getReconnectStrategy(); + final long clientConnectionTimeoutMillis = getConnectionTimeoutMillis(); return NetconfReconnectingClientConfigurationBuilder.create() .withAddress(socketAddress) .withConnectionTimeoutMillis(clientConnectionTimeoutMillis) .withReconnectStrategy(strategy) - .withSessionListener(new NetconfDeviceListener(device)) + .withSessionListener(listener) .withAuthHandler(new LoginPassword(getUsername(),getPassword())) .withProtocol(getTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP : @@ -174,19 +209,19 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co } private ReconnectStrategy getReconnectStrategy() { - Long connectionAttempts; + final Long connectionAttempts; if (getMaxConnectionAttempts() != null && getMaxConnectionAttempts() > 0) { connectionAttempts = getMaxConnectionAttempts(); } else { logger.trace("Setting {} on {} to infinity", maxConnectionAttemptsJmxAttribute, this); connectionAttempts = null; } - double sleepFactor = 1.5; - int minSleep = 1000; - Long maxSleep = null; - Long deadline = null; + final double sleepFactor = getSleepFactor().doubleValue(); + final int minSleep = getBetweenAttemptsTimeoutMillis(); + final Long maxSleep = null; + final Long deadline = null; - return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, getBetweenAttemptsTimeoutMillis(), + return new TimedReconnectStrategy(getEventExecutorDependency(), getBetweenAttemptsTimeoutMillis(), minSleep, sleepFactor, maxSleep, connectionAttempts, deadline); } @@ -199,7 +234,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co addressValue = getAddress().getIpv6Address().getValue(); } */ - InetAddress inetAddress = InetAddresses.forString(getAddress()); + final InetAddress inetAddress = InetAddresses.forString(getAddress()); return new InetSocketAddress(inetAddress, getPort().intValue()); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java new file mode 100644 index 0000000000..7a392a8769 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; + +public interface MessageTransformer extends SchemaContextListener { + + CompositeNode toNotification(M message); + + M toRpcRequest(QName rpc, CompositeNode node); + + RpcResult toRpcResult(M message, QName rpc); + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java new file mode 100644 index 0000000000..e0d24331a7 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +/** + * + */ +public interface RemoteDevice { + + void onRemoteSessionUp(PREF remoteSessionCapabilities, RemoteDeviceCommunicator listener); + + void onRemoteSessionDown(); + + void onNotification(M notification); +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java new file mode 100644 index 0000000000..67cb29abcf --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; + +import com.google.common.util.concurrent.ListenableFuture; + +public interface RemoteDeviceCommunicator extends AutoCloseable { + + ListenableFuture> sendRequest(M message, QName rpc); + + void close(); +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java new file mode 100644 index 0000000000..b2845d5533 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; + +public interface RemoteDeviceHandler extends AutoCloseable { + + void onDeviceConnected(SchemaContextProvider remoteSchemaContextProvider, + PREF netconfSessionPreferences, RpcImplementation deviceRpc); + + void onDeviceDisconnected(); + + void onNotification(CompositeNode domNotification); + + void close(); +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java new file mode 100644 index 0000000000..43577c3c26 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +import java.io.InputStream; +import java.util.Collection; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; + +public interface SchemaContextProviderFactory { + + SchemaContextProvider createContextProvider(Collection capabilities, SchemaSourceProvider sourceProvider); + +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java new file mode 100644 index 0000000000..7037231c5f --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.api; + +import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; + +public interface SchemaSourceProviderFactory { + + SchemaSourceProvider createSourceProvider(final RpcImplementation deviceRpc); +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java new file mode 100644 index 0000000000..022021d46d --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java @@ -0,0 +1,6 @@ +/** + * General API for remote connectors e.g. netconf connector + * + * TODO extract into separate bundle when another connector is implemented e.g. restconf connector + */ +package org.opendaylight.controller.sal.connect.api; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java deleted file mode 100644 index e6dc59cc10..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; - -import java.net.URI; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InventoryUtils { - private static final Logger LOG = LoggerFactory.getLogger(InventoryUtils.class); - private static final URI INVENTORY_NAMESPACE = URI.create("urn:opendaylight:inventory"); - private static final URI NETCONF_INVENTORY_NAMESPACE = URI.create("urn:opendaylight:netconf-node-inventory"); - private static final Date INVENTORY_REVISION = dateFromString("2013-08-19"); - private static final Date NETCONF_INVENTORY_REVISION = dateFromString("2014-01-08"); - public static final QName INVENTORY_NODES = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "nodes"); - public static final QName INVENTORY_NODE = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "node"); - public static final QName INVENTORY_ID = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "id"); - public static final QName INVENTORY_CONNECTED = new QName(NETCONF_INVENTORY_NAMESPACE, NETCONF_INVENTORY_REVISION, - "connected"); - public static final QName NETCONF_INVENTORY_INITIAL_CAPABILITY = new QName(NETCONF_INVENTORY_NAMESPACE, - NETCONF_INVENTORY_REVISION, "initial-capability"); - - public static final InstanceIdentifier INVENTORY_PATH = InstanceIdentifier.builder().node(INVENTORY_NODES) - .toInstance(); - public static final QName NETCONF_INVENTORY_MOUNT = null; - - private InventoryUtils() { - throw new UnsupportedOperationException("Utility class cannot be instantiated"); - } - - /** - * Converts date in string format yyyy-MM-dd to java.util.Date. - * - * @return java.util.Date conformant to string formatted date yyyy-MM-dd. - */ - private static Date dateFromString(final String date) { - // We do not reuse the formatter because it's not thread-safe - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - try { - return formatter.parse(date); - } catch (ParseException e) { - LOG.error("Failed to parse date {}", date, e); - return null; - } - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index 94beaed0df..dca8fcafef 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -7,541 +7,142 @@ */ package org.opendaylight.controller.sal.connect.netconf; -import static com.google.common.base.Preconditions.checkState; -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_CONNECTED; -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_ID; -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_NODE; -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_PATH; -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.NETCONF_INVENTORY_INITIAL_CAPABILITY; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.CONFIG_SOURCE_RUNNING; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_DATA_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_CONFIG_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toFilterStructure; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toRpcMessage; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.wrap; - -import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; -import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; -import org.opendaylight.controller.md.sal.common.api.data.DataModification; -import org.opendaylight.controller.md.sal.common.api.data.DataReader; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; -import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; -import org.opendaylight.controller.sal.binding.api.data.DataProviderService; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; -import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.sal.connect.api.MessageTransformer; +import org.opendaylight.controller.sal.connect.api.RemoteDevice; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; +import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; +import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; +import org.opendaylight.controller.sal.connect.netconf.schema.NetconfDeviceSchemaProviderFactory; +import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaSourceProvider; +import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.controller.sal.core.api.data.DataBrokerService; -import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction; -import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; -import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; -import org.opendaylight.protocol.framework.ReconnectStrategy; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode; -import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider; import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; -import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.ListenableFuture; -import io.netty.util.concurrent.EventExecutor; - -public class NetconfDevice implements Provider, // - DataReader, // - DataCommitHandler, // - RpcImplementation, // - AutoCloseable { - - InetSocketAddress socketAddress; - - MountProvisionInstance mountInstance; - - EventExecutor eventExecutor; - - ExecutorService processingExecutor; - - InstanceIdentifier path; - - ReconnectStrategy reconnectStrategy; - - AbstractCachingSchemaSourceProvider schemaSourceProvider; - - private NetconfDeviceSchemaContextProvider deviceContextProvider; - - protected Logger logger; - - Registration> operReaderReg; - Registration> confReaderReg; - Registration> commitHandlerReg; - List rpcReg; - - String name; - - MountProvisionService mountService; - - NetconfClientDispatcher dispatcher; - - static InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance(); - - SchemaSourceProvider remoteSourceProvider; - - private volatile DataBrokerService dataBroker; - - NetconfDeviceListener listener; - - private boolean rollbackSupported; - - private NetconfReconnectingClientConfiguration clientConfig; - private volatile DataProviderService dataProviderService; - - public NetconfDevice(String name) { - this.name = name; - this.logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + name); - this.path = InstanceIdentifier.builder(INVENTORY_PATH) - .nodeWithKey(INVENTORY_NODE, Collections.singletonMap(INVENTORY_ID, name)).toInstance(); - } - - public void start() { - checkState(dispatcher != null, "Dispatcher must be set."); - checkState(schemaSourceProvider != null, "Schema Source Provider must be set."); - checkState(eventExecutor != null, "Event executor must be set."); - - Preconditions.checkArgument(clientConfig.getSessionListener() instanceof NetconfDeviceListener); - listener = (NetconfDeviceListener) clientConfig.getSessionListener(); - - logger.info("Starting NETCONF Client {} for address {}", name, socketAddress); - - dispatcher.createReconnectingClient(clientConfig); - } - - Optional getSchemaContext() { - if (deviceContextProvider == null) { - return Optional.absent(); - } - return deviceContextProvider.currentContext; - } - - void bringDown() { - if (rpcReg != null) { - for (RpcRegistration reg : rpcReg) { - reg.close(); - } - rpcReg = null; - } - closeGracefully(confReaderReg); - confReaderReg = null; - closeGracefully(operReaderReg); - operReaderReg = null; - closeGracefully(commitHandlerReg); - commitHandlerReg = null; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; - updateDeviceState(false, Collections. emptySet()); - } +/** + * This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade + */ +public final class NetconfDevice implements RemoteDevice { - private void closeGracefully(final AutoCloseable resource) { - if (resource != null) { - try { - resource.close(); - } catch (Exception e) { - logger.warn("Ignoring exception while closing {}", resource, e); - } - } - } + private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class); - void bringUp(final SchemaSourceProvider delegate, final Set capabilities, final boolean rollbackSupported) { - // This has to be called from separate thread, not from netty thread calling onSessionUp in DeviceListener. - // Reason: delegate.getSchema blocks thread when waiting for response - // however, if the netty thread is blocked, no incoming message can be processed - // ... netty should pick another thread from pool to process incoming message, but it does not http://netty.io/wiki/thread-model.html - // TODO redesign +refactor - processingExecutor.submit(new Runnable() { - @Override - public void run() { - NetconfDevice.this.rollbackSupported = rollbackSupported; - remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate); - deviceContextProvider = new NetconfDeviceSchemaContextProvider(NetconfDevice.this, remoteSourceProvider); - deviceContextProvider.createContextFromCapabilities(capabilities); - if (mountInstance != null && getSchemaContext().isPresent()) { - mountInstance.setSchemaContext(getSchemaContext().get()); - } + private final RemoteDeviceId id; - updateDeviceState(true, capabilities); + private final RemoteDeviceHandler salFacade; + private final ListeningExecutorService processingExecutor; + private final MessageTransformer messageTransformer; + private final SchemaContextProviderFactory schemaContextProviderFactory; + private final SchemaSourceProviderFactory sourceProviderFactory; - if (mountInstance != null) { - confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, NetconfDevice.this); - operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, NetconfDevice.this); - commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, NetconfDevice.this); + public static NetconfDevice createNetconfDevice(final RemoteDeviceId id, + final AbstractCachingSchemaSourceProvider schemaSourceProvider, + final ExecutorService executor, final RemoteDeviceHandler salFacade) { - List rpcs = new ArrayList<>(); - // TODO same condition twice - if (mountInstance != null && getSchemaContext().isPresent()) { - for (RpcDefinition rpc : mountInstance.getSchemaContext().getOperations()) { - rpcs.add(mountInstance.addRpcImplementation(rpc.getQName(), NetconfDevice.this)); - } + return new NetconfDevice(id, salFacade, executor, new NetconfMessageTransformer(), + new NetconfDeviceSchemaProviderFactory(id), new SchemaSourceProviderFactory() { + @Override + public SchemaSourceProvider createSourceProvider(final RpcImplementation deviceRpc) { + return schemaSourceProvider.createInstanceFor(new NetconfRemoteSchemaSourceProvider(id, + deviceRpc)); } - rpcReg = rpcs; - } - } - }); - } - - private void updateDeviceState(boolean up, Set capabilities) { - checkDataStoreState(); - - DataModificationTransaction transaction = dataBroker.beginTransaction(); - - CompositeNodeBuilder it = ImmutableCompositeNode.builder(); - it.setQName(INVENTORY_NODE); - it.addLeaf(INVENTORY_ID, name); - it.addLeaf(INVENTORY_CONNECTED, up); - - logger.debug("Client capabilities {}", capabilities); - for (QName capability : capabilities) { - it.addLeaf(NETCONF_INVENTORY_INITIAL_CAPABILITY, capability.toString()); - } - - logger.debug("Update device state transaction " + transaction.getIdentifier() - + " putting operational data started."); - transaction.removeOperationalData(path); - transaction.putOperationalData(path, it.toInstance()); - logger.debug("Update device state transaction " + transaction.getIdentifier() - + " putting operational data ended."); - - // FIXME: this has to be asynchronous - RpcResult transactionStatus = null; - try { - transactionStatus = transaction.commit().get(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while waiting for response", e); - } catch (ExecutionException e) { - throw new RuntimeException("Read configuration data " + path + " failed", e); - } - // TODO better ex handling - - if (transactionStatus.isSuccessful()) { - logger.debug("Update device state transaction " + transaction.getIdentifier() + " SUCCESSFUL."); - } else { - logger.debug("Update device state transaction " + transaction.getIdentifier() + " FAILED!"); - logger.debug("Update device state transaction status " + transaction.getStatus()); - } - } - - @Override - public CompositeNode readConfigurationData(InstanceIdentifier path) { - RpcResult result = null; - try { - result = this.invokeRpc(NETCONF_GET_CONFIG_QNAME, - wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))).get(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while waiting for response", e); - } catch (ExecutionException e) { - throw new RuntimeException("Read configuration data " + path + " failed", e); - } - - CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); - return data == null ? null : (CompositeNode) findNode(data, path); - } - - @Override - public CompositeNode readOperationalData(InstanceIdentifier path) { - RpcResult result = null; - try { - result = invokeRpc(NETCONF_GET_QNAME, wrap(NETCONF_GET_QNAME, toFilterStructure(path))).get(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while waiting for response", e); - } catch (ExecutionException e) { - throw new RuntimeException("Read configuration data " + path + " failed", e); - } - - CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); - return (CompositeNode) findNode(data, path); - } - - @Override - public Set getSupportedRpcs() { - return Collections.emptySet(); - } - - @Override - public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { - return listener.sendRequest(toRpcMessage(rpc, input, getSchemaContext()), rpc); + }); } - @Override - public Collection getProviderFunctionality() { - return Collections.emptySet(); + @VisibleForTesting + protected NetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final ExecutorService processingExecutor, final MessageTransformer messageTransformer, + final SchemaContextProviderFactory schemaContextProviderFactory, + final SchemaSourceProviderFactory sourceProviderFactory) { + this.id = id; + this.messageTransformer = messageTransformer; + this.salFacade = salFacade; + this.sourceProviderFactory = sourceProviderFactory; + this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor); + this.schemaContextProviderFactory = schemaContextProviderFactory; } @Override - public void onSessionInitiated(ProviderSession session) { - dataBroker = session.getService(DataBrokerService.class); - - processingExecutor.submit(new Runnable() { + public void onRemoteSessionUp(final NetconfSessionCapabilities remoteSessionCapabilities, + final RemoteDeviceCommunicator listener) { + // SchemaContext setup has to be performed in a dedicated thread since + // we are in a netty thread in this method + // Yang models are being downloaded in this method and it would cause a + // deadlock if we used the netty thread + // http://netty.io/wiki/thread-model.html + logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities); + + final ListenableFuture salInitializationFuture = processingExecutor.submit(new Runnable() { @Override public void run() { - updateInitialState(); + final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(remoteSessionCapabilities, listener); + final SchemaSourceProvider delegate = sourceProviderFactory.createSourceProvider(deviceRpc); + final SchemaContextProvider schemaContextProvider = setUpSchemaContext(delegate, remoteSessionCapabilities); + updateMessageTransformer(schemaContextProvider); + salFacade.onDeviceConnected(schemaContextProvider, remoteSessionCapabilities, deviceRpc); } }); - mountService = session.getService(MountProvisionService.class); - if (mountService != null) { - mountInstance = mountService.createOrGetMountPoint(path); - } - } - - private void updateInitialState() { - checkDataStoreState(); - - DataModificationTransaction transaction = dataBroker.beginTransaction(); - if (operationalNodeNotExisting(transaction)) { - transaction.putOperationalData(path, getNodeWithId()); - } - if (configurationNodeNotExisting(transaction)) { - transaction.putConfigurationData(path, getNodeWithId()); - } - - try { - transaction.commit().get(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while waiting for response", e); - } catch (ExecutionException e) { - throw new RuntimeException("Read configuration data " + path + " failed", e); - } - } - - private void checkDataStoreState() { - // read data from Nodes/Node in order to wait with write until schema for Nodes/Node is present in datastore - dataProviderService.readOperationalData(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.builder( - Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class).augmentation(NetconfNode.class).build()); } - - CompositeNode getNodeWithId() { - SimpleNodeTOImpl id = new SimpleNodeTOImpl(INVENTORY_ID, null, name); - return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.> singletonList(id)); - } - - boolean configurationNodeNotExisting(DataModificationTransaction transaction) { - return null == transaction.readConfigurationData(path); - } - - boolean operationalNodeNotExisting(DataModificationTransaction transaction) { - return null == transaction.readOperationalData(path); - } - - static Node findNode(CompositeNode node, InstanceIdentifier identifier) { - - Node current = node; - for (InstanceIdentifier.PathArgument arg : identifier.getPath()) { - if (current instanceof SimpleNode) { - return null; - } else if (current instanceof CompositeNode) { - CompositeNode currentComposite = (CompositeNode) current; - - current = currentComposite.getFirstCompositeByName(arg.getNodeType()); - if (current == null) { - current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision()); - } - if (current == null) { - current = currentComposite.getFirstSimpleByName(arg.getNodeType()); - } - if (current == null) { - current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision()); - } - if (current == null) { - return null; - } - } - } - return current; - } - - @Override - public DataCommitTransaction requestCommit( - DataModification modification) { - NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, - modification, true, rollbackSupported); - try { - twoPhaseCommit.prepare(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while waiting for response", e); - } catch (ExecutionException e) { - throw new RuntimeException("Read configuration data " + path + " failed", e); - } - return twoPhaseCommit; - } - - Set getCapabilities(Collection capabilities) { - return FluentIterable.from(capabilities).filter(new Predicate() { + Futures.addCallback(salInitializationFuture, new FutureCallback() { @Override - public boolean apply(final String capability) { - return capability.contains("?") && capability.contains("module=") && capability.contains("revision="); + public void onSuccess(final Object result) { + logger.debug("{}: Initialization in sal successful", id); + logger.info("{}: Netconf connector initialized successfully", id); } - }).transform(new Function() { - @Override - public QName apply(final String capability) { - String[] parts = capability.split("\\?"); - String namespace = parts[0]; - FluentIterable queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&"))); - - String revision = getStringAndTransform(queryParams, "revision=", "revision="); - String moduleName = getStringAndTransform(queryParams, "module=", "module="); - - if (revision == null) { - logger.warn("Netconf device was not reporting revision correctly, trying to get amp;revision="); - revision = getStringAndTransform(queryParams, "amp;revision==", "revision="); - - if (revision != null) { - logger.warn("Netconf device returned revision incorectly escaped for {}", capability); - } - } - if (revision == null) { - return QName.create(URI.create(namespace), null, moduleName); - } - return QName.create(namespace, revision, moduleName); - } - - private String getStringAndTransform(final Iterable queryParams, final String match, - final String substringToRemove) { - Optional found = Iterables.tryFind(queryParams, new Predicate() { - @Override - public boolean apply(final String input) { - return input.startsWith(match); - } - }); - - return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null; + @Override + public void onFailure(final Throwable t) { + // Unable to initialize device, set as disconnected + logger.error("{}: Initialization failed", id, t); + salFacade.onDeviceDisconnected(); } - - }).toSet(); - } - - @Override - public void close() { - bringDown(); - } - - public String getName() { - return name; - } - - public InetSocketAddress getSocketAddress() { - return socketAddress; - } - - public MountProvisionInstance getMountInstance() { - return mountInstance; - } - - public void setReconnectStrategy(final ReconnectStrategy reconnectStrategy) { - this.reconnectStrategy = reconnectStrategy; - } - - public void setProcessingExecutor(final ExecutorService processingExecutor) { - this.processingExecutor = processingExecutor; - } - - public void setSocketAddress(final InetSocketAddress socketAddress) { - this.socketAddress = socketAddress; - } - - public void setEventExecutor(final EventExecutor eventExecutor) { - this.eventExecutor = eventExecutor; - } - - public void setSchemaSourceProvider(final AbstractCachingSchemaSourceProvider schemaSourceProvider) { - this.schemaSourceProvider = schemaSourceProvider; - } - - public void setDispatcher(final NetconfClientDispatcher dispatcher) { - this.dispatcher = dispatcher; + }); } - public void setClientConfig(final NetconfReconnectingClientConfiguration clientConfig) { - this.clientConfig = clientConfig; + /** + * Update initial message transformer to use retrieved schema + */ + private void updateMessageTransformer(final SchemaContextProvider schemaContextProvider) { + messageTransformer.onGlobalContextUpdated(schemaContextProvider.getSchemaContext()); } - public void setDataProviderService(final DataProviderService dataProviderService) { - this.dataProviderService = dataProviderService; + private SchemaContextProvider setUpSchemaContext(final SchemaSourceProvider sourceProvider, final NetconfSessionCapabilities capabilities) { + return schemaContextProviderFactory.createContextProvider(capabilities.getModuleBasedCaps(), sourceProvider); } -} - -class NetconfDeviceSchemaContextProvider { - - NetconfDevice device; - - SchemaSourceProvider sourceProvider; - Optional currentContext; - - NetconfDeviceSchemaContextProvider(NetconfDevice device, SchemaSourceProvider sourceProvider) { - this.device = device; - this.sourceProvider = sourceProvider; - this.currentContext = Optional.absent(); + private NetconfDeviceRpc setUpDeviceRpc(final NetconfSessionCapabilities capHolder, final RemoteDeviceCommunicator listener) { + Preconditions.checkArgument(capHolder.isMonitoringSupported(), + "%s: Netconf device does not support netconf monitoring, yang schemas cannot be acquired. Netconf device capabilities", capHolder); + return new NetconfDeviceRpc(listener, messageTransformer); } - void createContextFromCapabilities(Iterable capabilities) { - YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider); - if (!sourceContext.getMissingSources().isEmpty()) { - device.logger.warn("Sources for following models are missing {}", sourceContext.getMissingSources()); - } - device.logger.debug("Trying to create schema context from {}", sourceContext.getValidSources()); - List modelsToParse = YangSourceContext.getValidInputStreams(sourceContext); - if (!sourceContext.getValidSources().isEmpty()) { - SchemaContext schemaContext = tryToCreateContext(modelsToParse); - currentContext = Optional.fromNullable(schemaContext); - } else { - currentContext = Optional.absent(); - } - if (currentContext.isPresent()) { - device.logger.debug("Schema context successfully created."); - } + @Override + public void onRemoteSessionDown() { + salFacade.onDeviceDisconnected(); } - SchemaContext tryToCreateContext(List modelsToParse) { - YangParserImpl parser = new YangParserImpl(); - try { - - Set models = parser.parseYangModelsFromStreams(modelsToParse); - return parser.resolveSchemaContext(models); - } catch (Exception e) { - device.logger.debug("Error occured during parsing YANG schemas", e); - return null; - } + @Override + public void onNotification(final NetconfMessage notification) { + final CompositeNode parsedNotification = messageTransformer.toNotification(notification); + salFacade.onNotification(parsedNotification); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java deleted file mode 100644 index 68667f0143..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; - -import com.google.common.collect.Sets; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; - -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Queue; -import java.util.Set; - -import org.opendaylight.controller.netconf.api.NetconfDocumentedException; -import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.api.NetconfTerminationReason; -import org.opendaylight.controller.netconf.client.NetconfClientSession; -import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; -import org.opendaylight.controller.netconf.util.xml.XmlElement; -import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.opendaylight.controller.sal.common.util.Rpcs; -import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -public class NetconfDeviceListener implements NetconfClientSessionListener { - private static final class Request { - final UncancellableFuture> future; - final NetconfMessage request; - final QName rpc; - - private Request(UncancellableFuture> future, NetconfMessage request, final QName rpc) { - this.future = future; - this.request = request; - this.rpc = rpc; - } - } - - private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceListener.class); - private final Queue requests = new ArrayDeque<>(); - private final NetconfDevice device; - private NetconfClientSession session; - - public NetconfDeviceListener(final NetconfDevice device) { - this.device = Preconditions.checkNotNull(device); - } - - @Override - public synchronized void onSessionUp(final NetconfClientSession session) { - LOG.debug("Session with {} established as address {} session-id {}", - device.getName(), device.getSocketAddress(), session.getSessionId()); - - this.session = session; - - final Set caps = device.getCapabilities(session.getServerCapabilities()); - LOG.trace("Server {} advertized capabilities {}", device.getName(), caps); - - // Select the appropriate provider - final SchemaSourceProvider delegate; - if (NetconfRemoteSchemaSourceProvider.isSupportedFor(caps)) { - delegate = new NetconfRemoteSchemaSourceProvider(device); - // FIXME caps do not contain urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring, since it is filtered out in getCapabilitites - } else if(session.getServerCapabilities().contains(NetconfRemoteSchemaSourceProvider.IETF_NETCONF_MONITORING.getNamespace().toString())) { - delegate = new NetconfRemoteSchemaSourceProvider(device); - } else { - LOG.info("Netconf server {} does not support IETF Netconf Monitoring", device.getName()); - delegate = SchemaSourceProviders.noopProvider(); - } - - device.bringUp(delegate, caps, isRollbackSupported(session.getServerCapabilities())); - - } - - private static boolean isRollbackSupported(final Collection serverCapabilities) { - // TODO rollback capability cannot be searched for in Set caps - // since this set does not contain module-less capabilities - return Sets.newHashSet(serverCapabilities).contains(NetconfMapping.NETCONF_ROLLBACK_ON_ERROR_URI.toString()); - } - - private synchronized void tearDown(final Exception e) { - session = null; - - /* - * Walk all requests, check if they have been executing - * or cancelled and remove them from the queue. - */ - final Iterator it = requests.iterator(); - while (it.hasNext()) { - final Request r = it.next(); - if (r.future.isUncancellable()) { - // FIXME: add a RpcResult instead? - r.future.setException(e); - it.remove(); - } else if (r.future.isCancelled()) { - // This just does some house-cleaning - it.remove(); - } - } - - device.bringDown(); - } - - @Override - public void onSessionDown(final NetconfClientSession session, final Exception e) { - LOG.debug("Session with {} went down", device.getName(), e); - tearDown(e); - } - - @Override - public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) { - LOG.debug("Session with {} terminated {}", session, reason); - tearDown(new RuntimeException(reason.getErrorMessage())); - } - - @Override - public void onMessage(final NetconfClientSession session, final NetconfMessage message) { - /* - * Dispatch between notifications and messages. Messages need to be processed - * with lock held, notifications do not. - */ - if (isNotification(message)) { - processNotification(message); - } else { - processMessage(message); - } - } - - private synchronized void processMessage(final NetconfMessage message) { - final Request r = requests.peek(); - if (r.future.isUncancellable()) { - requests.poll(); - LOG.debug("Matched {} to {}", r.request, message); - - try { - NetconfMapping.checkValidReply(r.request, message); - } catch (IllegalStateException e) { - LOG.warn("Invalid request-reply match, reply message contains different message-id", e); - r.future.setException(e); - return; - } - - try { - NetconfMapping.checkSuccessReply(message); - } catch (NetconfDocumentedException | IllegalStateException e) { - LOG.warn("Error reply from remote device", e); - r.future.setException(e); - return; - } - - r.future.set(NetconfMapping.toRpcResult(message, r.rpc, device.getSchemaContext())); - } else { - LOG.warn("Ignoring unsolicited message", message); - } - } - - synchronized ListenableFuture> sendRequest(final NetconfMessage message, final QName rpc) { - if (session == null) { - LOG.debug("Session to {} is disconnected, failing RPC request {}", device.getName(), message); - return Futures.>immediateFuture(new RpcResult() { - @Override - public boolean isSuccessful() { - return false; - } - - @Override - public CompositeNode getResult() { - return null; - } - - @Override - public Collection getErrors() { - // FIXME: indicate that the session is down - return Collections.emptySet(); - } - }); - } - - final Request req = new Request(new UncancellableFuture>(true), message, rpc); - requests.add(req); - - session.sendMessage(req.request).addListener(new FutureListener() { - @Override - public void operationComplete(final Future future) throws Exception { - if (!future.isSuccess()) { - // We expect that a session down will occur at this point - LOG.debug("Failed to send request {}", XmlUtil.toString(req.request.getDocument()), future.cause()); - req.future.setException(future.cause()); - } else { - LOG.trace("Finished sending request {}", req.request); - } - } - }); - - return req.future; - } - - /** - * Process an incoming notification. - * - * @param notification Notification message - */ - private void processNotification(final NetconfMessage notification) { - this.device.logger.debug("Received NETCONF notification.", notification); - CompositeNode domNotification = NetconfMapping.toNotificationNode(notification, device.getSchemaContext()); - if (domNotification == null) { - return; - } - - MountProvisionInstance mountInstance = this.device.getMountInstance(); - if (mountInstance != null) { - mountInstance.publish(domNotification); - } - } - - private static boolean isNotification(final NetconfMessage message) { - final XmlElement xmle = XmlElement.fromDomDocument(message.getDocument()); - return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName()) ; - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java deleted file mode 100644 index 34cd9aa47b..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; - -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CANDIDATE_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_COMMIT_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CONFIG_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_EDIT_CONFIG_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_ERROR_OPTION_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_OPERATION_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_RUNNING_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_TARGET_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.ROLLBACK_ON_ERROR_OPTION; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ExecutionException; - -import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; -import org.opendaylight.controller.md.sal.common.api.data.DataModification; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction { - private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class); - private final DataModification modification; - private final NetconfDevice device; - private final boolean candidateSupported; - private final boolean rollbackSupported; - - public NetconfDeviceTwoPhaseCommitTransaction(final NetconfDevice device, - final DataModification modification, - final boolean candidateSupported, final boolean rollbackOnErrorSupported) { - this.device = Preconditions.checkNotNull(device); - this.modification = Preconditions.checkNotNull(modification); - this.candidateSupported = candidateSupported; - this.rollbackSupported = rollbackOnErrorSupported; - } - - void prepare() throws InterruptedException, ExecutionException { - for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) { - sendDelete(toRemove); - } - for(Entry toUpdate : modification.getUpdatedConfigurationData().entrySet()) { - sendMerge(toUpdate.getKey(),toUpdate.getValue()); - } - } - - private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException { - sendEditRpc(createEditStructure(key, Optional.absent(), Optional.of(value))); - } - - private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException { - sendEditRpc(createEditStructure(toDelete, Optional.of("delete"), Optional. absent())); - } - - private void sendEditRpc(final CompositeNode editStructure) throws InterruptedException, ExecutionException { - CompositeNodeBuilder builder = configurationRpcBuilder(); - builder.setQName(NETCONF_EDIT_CONFIG_QNAME); - builder.add(editStructure); - - RpcResult rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance()).get(); - Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful"); - } - - private CompositeNodeBuilder configurationRpcBuilder() { - CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); - - Node targetNode; - if(candidateSupported) { - targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.>of()); - } else { - targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.>of()); - } - - Node targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.>of(targetNode)); - - if(rollbackSupported) { - LOG.debug("Rollback-on-error supported, setting {} to {}", NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); - ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); - } - - ret.add(targetWrapperNode); - return ret; - } - - private CompositeNode createEditStructure(final InstanceIdentifier dataPath, final Optional operation, - final Optional lastChildOverride) { - List path = dataPath.getPath(); - List reversed = Lists.reverse(path); - CompositeNode previous = null; - boolean isLast = true; - for (PathArgument arg : reversed) { - CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); - builder.setQName(arg.getNodeType()); - Map predicates = Collections.emptyMap(); - if (arg instanceof NodeIdentifierWithPredicates) { - predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues(); - } - for (Entry entry : predicates.entrySet()) { - builder.addLeaf(entry.getKey(), entry.getValue()); - } - - if (isLast) { - if (operation.isPresent()) { - builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get()); - } - if (lastChildOverride.isPresent()) { - List> children = lastChildOverride.get().getValue(); - for(Node child : children) { - if(!predicates.containsKey(child.getKey())) { - builder.add(child); - } - } - - } - } else { - builder.add(previous); - } - previous = builder.toInstance(); - isLast = false; - } - return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.>of(previous)); - } - - @Override - public RpcResult finish() { - CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); - commitInput.setQName(NETCONF_COMMIT_QNAME); - try { - final RpcResult rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance()).get(); - return new RpcResult() { - - @Override - public boolean isSuccessful() { - return rpcResult.isSuccessful(); - } - - @Override - public Void getResult() { - return null; - } - - @Override - public Collection getErrors() { - return rpcResult.getErrors(); - } - }; - } catch (final InterruptedException | ExecutionException e) { - LOG.warn("Failed to finish operation", e); - return new RpcResult() { - @Override - public boolean isSuccessful() { - return false; - } - - @Override - public Void getResult() { - return null; - } - - @Override - public Collection getErrors() { - // FIXME: wrap the exception - return Collections.emptySet(); - } - }; - } - } - - @Override - public DataModification getModification() { - return this.modification; - } - - @Override - public RpcResult rollback() throws IllegalStateException { - // TODO Auto-generated method stub - return null; - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java deleted file mode 100644 index b68f18f52e..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; - -public class NetconfInventoryUtils { - public static final QName NETCONF_MOUNT = null; - public static final QName NETCONF_ENDPOINT = null; - public static final QName NETCONF_ENDPOINT_ADDRESS = null; - public static final QName NETCONF_ENDPOINT_PORT = null; - - private NetconfInventoryUtils() { - throw new UnsupportedOperationException("Utility class cannot be instantiated"); - } - - public static String getEndpointAddress(CompositeNode node) { - return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_ADDRESS).getValue().toString(); - } - - public static String getEndpointPort(CompositeNode node) { - return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_PORT).getValue().toString(); - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java deleted file mode 100644 index 31c6bd0138..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; - -import java.util.Collection; -import java.util.concurrent.ExecutionException; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider { - - public static final QName IETF_NETCONF_MONITORING = QName.create( - "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring"); - public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema"); - public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data"); - - private final NetconfDevice device; - - private final Logger logger; - - public NetconfRemoteSchemaSourceProvider(NetconfDevice device) { - this.device = Preconditions.checkNotNull(device); - logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + device.getName()); - } - - @Override - public Optional getSchemaSource(String moduleName, Optional revision) { - CompositeNodeBuilder request = ImmutableCompositeNode.builder(); // - request.setQName(GET_SCHEMA_QNAME) // - .addLeaf("format", "yang") // - .addLeaf("identifier", moduleName); // - if (revision.isPresent()) { - request.addLeaf("version", revision.get()); - } - - logger.trace("Loading YANG schema source for {}:{}", moduleName, revision); - try { - RpcResult schemaReply = device.invokeRpc(GET_SCHEMA_QNAME, request.toInstance()).get(); - if (schemaReply.isSuccessful()) { - String schemaBody = getSchemaFromRpc(schemaReply.getResult()); - if (schemaBody != null) { - device.logger.trace("YANG Schema successfully retrieved from remote for {}:{}", moduleName, revision); - return Optional.of(schemaBody); - } - } - logger.warn("YANG shcema was not successfully retrieved. Errors: {}", schemaReply.getErrors()); - } catch (InterruptedException | ExecutionException e) { - logger.warn("YANG shcema was not successfully retrieved.", e); - } - return Optional.absent(); - } - - private String getSchemaFromRpc(CompositeNode result) { - if (result == null) { - return null; - } - SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); - Object potential = simpleNode.getValue(); - if (potential instanceof String) { - return (String) potential; - } - return null; - } - - public static final boolean isSupportedFor(Collection capabilities) { - return capabilities.contains(IETF_NETCONF_MONITORING); - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java new file mode 100644 index 0000000000..fa6e252293 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.listener; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Iterator; +import java.util.Queue; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.sal.common.util.RpcErrors; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connect.api.RemoteDevice; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.FailedRpcResult; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; + +public class NetconfDeviceCommunicator implements NetconfClientSessionListener, RemoteDeviceCommunicator { + + private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class); + + private static final RpcResult FAILED_RPC_RESULT = new FailedRpcResult<>(RpcErrors.getRpcError( + null, null, null, RpcError.ErrorSeverity.ERROR, "Netconf session disconnected", + RpcError.ErrorType.PROTOCOL, null)); + + private final RemoteDevice remoteDevice; + private final RemoteDeviceId id; + + public NetconfDeviceCommunicator(final RemoteDeviceId id, + final RemoteDevice remoteDevice) { + this.id = id; + this.remoteDevice = remoteDevice; + } + + private final Queue requests = new ArrayDeque<>(); + private NetconfClientSession session; + + @Override + public synchronized void onSessionUp(final NetconfClientSession session) { + logger.debug("{}: Session established", id); + this.session = session; + + final NetconfSessionCapabilities netconfSessionCapabilities = NetconfSessionCapabilities.fromNetconfSession(session); + logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities); + + remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this); + } + + public void initializeRemoteConnection(final NetconfClientDispatcher dispatch, + final NetconfReconnectingClientConfiguration config) { + dispatch.createReconnectingClient(config); + } + + private synchronized void tearDown(final Exception e) { + remoteDevice.onRemoteSessionDown(); + session = null; + + /* + * Walk all requests, check if they have been executing + * or cancelled and remove them from the queue. + */ + final Iterator it = requests.iterator(); + while (it.hasNext()) { + final Request r = it.next(); + if (r.future.isUncancellable()) { + r.future.setException(e); + it.remove(); + } else if (r.future.isCancelled()) { + // This just does some house-cleaning + it.remove(); + } + } + } + + @Override + public void onSessionDown(final NetconfClientSession session, final Exception e) { + logger.warn("{}: Session went down", id, e); + tearDown(e); + } + + @Override + public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) { + logger.warn("{}: Session terminated {}", id, reason); + tearDown(new RuntimeException(reason.getErrorMessage())); + } + + @Override + public void onMessage(final NetconfClientSession session, final NetconfMessage message) { + /* + * Dispatch between notifications and messages. Messages need to be processed + * with lock held, notifications do not. + */ + if (isNotification(message)) { + processNotification(message); + } else { + processMessage(message); + } + } + + private synchronized void processMessage(final NetconfMessage message) { + final Request r = requests.peek(); + if (r.future.isUncancellable()) { + requests.poll(); + + logger.debug("{}: Message received {}", id, message); + + if(logger.isTraceEnabled()) { + logger.trace("{}: Matched request: {} to response: {}", id, msgToS(r.request), msgToS(message)); + } + + try { + NetconfMessageTransformUtil.checkValidReply(r.request, message); + } catch (final IllegalStateException e) { + logger.warn("{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id, + msgToS(r.request), msgToS(message), e); + r.future.setException(e); + return; + } + + try { + NetconfMessageTransformUtil.checkSuccessReply(message); + } catch (NetconfDocumentedException | IllegalStateException e) { + logger.warn("{}: Error reply from remote device, request: {}, response: {}", id, + msgToS(r.request), msgToS(message), e); + r.future.setException(e); + return; + } + + r.future.set(Rpcs.getRpcResult(true, message, Collections.emptySet())); + } else { + logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message)); + } + } + + @Override + public void close() { + tearDown(new RuntimeException("Closed")); + } + + private static String msgToS(final NetconfMessage msg) { + return XmlUtil.toString(msg.getDocument()); + } + + @Override + public synchronized ListenableFuture> sendRequest(final NetconfMessage message, final QName rpc) { + if(logger.isTraceEnabled()) { + logger.trace("{}: Sending message {}", id, msgToS(message)); + } + + if (session == null) { + logger.warn("{}: Session is disconnected, failing RPC request {}", id, message); + return Futures.immediateFuture(FAILED_RPC_RESULT); + } + + final Request req = new Request(new UncancellableFuture>(true), message, rpc); + requests.add(req); + + session.sendMessage(req.request).addListener(new FutureListener() { + @Override + public void operationComplete(final Future future) throws Exception { + if (!future.isSuccess()) { + // We expect that a session down will occur at this point + logger.debug("{}: Failed to send request {}", id, XmlUtil.toString(req.request.getDocument()), future.cause()); + req.future.setException(future.cause()); + } else { + logger.trace("{}: Finished sending request {}", id, req.request); + } + } + }); + + return req.future; + } + + private void processNotification(final NetconfMessage notification) { + logger.debug("{}: Notification received: {}", id, notification); + + if(logger.isTraceEnabled()) { + logger.trace("{}: Notification received: {}", id, msgToS(notification)); + } + + remoteDevice.onNotification(notification); + } + + private static boolean isNotification(final NetconfMessage message) { + final XmlElement xmle = XmlElement.fromDomDocument(message.getDocument()); + return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName()) ; + } + + private static final class Request { + final UncancellableFuture> future; + final NetconfMessage request; + final QName rpc; + + private Request(final UncancellableFuture> future, final NetconfMessage request, final QName rpc) { + this.future = future; + this.request = request; + this.rpc = rpc; + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java new file mode 100644 index 0000000000..82903ea4ec --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java @@ -0,0 +1,111 @@ +package org.opendaylight.controller.sal.connect.netconf.listener; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.yangtools.yang.common.QName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +public final class NetconfSessionCapabilities { + + private static final Logger logger = LoggerFactory.getLogger(NetconfSessionCapabilities.class); + + private final Set capabilities; + + private final Set moduleBasedCaps; + + private NetconfSessionCapabilities(final Set capabilities, final Set moduleBasedCaps) { + this.capabilities = capabilities; + this.moduleBasedCaps = moduleBasedCaps; + } + + public Set getModuleBasedCaps() { + return moduleBasedCaps; + } + + public boolean containsCapability(final String capability) { + return capabilities.contains(capability); + } + + public boolean containsCapability(final QName capability) { + return moduleBasedCaps.contains(capability); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("capabilities", capabilities) + .add("rollback", isRollbackSupported()) + .add("monitoring", isMonitoringSupported()) + .toString(); + } + + public boolean isRollbackSupported() { + return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()); + } + + public boolean isMonitoringSupported() { + return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING) + || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString()); + } + + public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) { + return fromStrings(session.getServerCapabilities()); + } + + public static NetconfSessionCapabilities fromStrings(final Collection capabilities) { + final Set moduleBasedCaps = Sets.newHashSet(); + + for (final String capability : capabilities) { + if(isModuleBasedCapability(capability)) { + final String[] parts = capability.split("\\?"); + final String namespace = parts[0]; + final FluentIterable queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&"))); + + String revision = getStringAndTransform(queryParams, "revision=", "revision="); + + final String moduleName = getStringAndTransform(queryParams, "module=", "module="); + + if (revision == null) { + logger.debug("Netconf device was not reporting revision correctly, trying to get amp;revision="); + revision = getStringAndTransform(queryParams, "amp;revision=", "amp;revision="); + + if (revision == null) { + logger.warn("Netconf device returned revision incorrectly escaped for {}", capability); + } + } + moduleBasedCaps.add(QName.create(namespace, revision, moduleName)); + } + } + + return new NetconfSessionCapabilities(Sets.newHashSet(capabilities), moduleBasedCaps); + } + + private static boolean isModuleBasedCapability(final String capability) { + return capability.contains("?") && capability.contains("module=") && capability.contains("revision="); + } + + private static String getStringAndTransform(final Iterable queryParams, final String match, + final String substringToRemove) { + final Optional found = Iterables.tryFind(queryParams, new Predicate() { + @Override + public boolean apply(final String input) { + return input.startsWith(match); + } + }); + + return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null; + } + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/UncancellableFuture.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/UncancellableFuture.java similarity index 67% rename from opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/UncancellableFuture.java rename to opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/UncancellableFuture.java index c353f86eb6..8d89ff8b93 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/UncancellableFuture.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/UncancellableFuture.java @@ -5,7 +5,7 @@ * 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.controller.sal.connect.netconf; +package org.opendaylight.controller.sal.connect.netconf.listener; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; @@ -17,7 +17,7 @@ final class UncancellableFuture extends AbstractFuture { @GuardedBy("this") private boolean uncancellable = false; - public UncancellableFuture(boolean uncancellable) { + public UncancellableFuture(final boolean uncancellable) { this.uncancellable = uncancellable; } @@ -35,23 +35,19 @@ final class UncancellableFuture extends AbstractFuture { } @Override - public synchronized boolean cancel(boolean mayInterruptIfRunning) { - if (uncancellable) { - return false; - } - - return super.cancel(mayInterruptIfRunning); + public synchronized boolean cancel(final boolean mayInterruptIfRunning) { + return uncancellable ? false : super.cancel(mayInterruptIfRunning); } @Override - public synchronized boolean set(@Nullable V value) { - Preconditions.checkState(uncancellable == true); + public synchronized boolean set(@Nullable final V value) { + Preconditions.checkState(uncancellable); return super.set(value); } @Override - protected boolean setException(Throwable throwable) { - Preconditions.checkState(uncancellable == true); + protected boolean setException(final Throwable throwable) { + Preconditions.checkState(uncancellable); return super.setException(throwable); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java new file mode 100644 index 0000000000..4a95582657 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java @@ -0,0 +1,4 @@ +/** + * Implementation of netconf southbound connector + */ +package org.opendaylight.controller.sal.connect.netconf; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java new file mode 100644 index 0000000000..457b8c36b9 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; +import org.opendaylight.controller.md.sal.common.api.data.DataModification; +import org.opendaylight.controller.sal.common.util.RpcErrors; +import org.opendaylight.controller.sal.connect.util.FailedRpcResult; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class NetconfDeviceCommitHandler implements DataCommitHandler { + + private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceCommitHandler.class); + + private final RemoteDeviceId id; + private final RpcImplementation rpc; + private final boolean rollbackSupported; + + public NetconfDeviceCommitHandler(final RemoteDeviceId id, final RpcImplementation rpc, final boolean rollbackSupported) { + this.id = id; + this.rpc = rpc; + this.rollbackSupported = rollbackSupported; + } + + @Override + public DataCommitTransaction requestCommit( + final DataModification modification) { + + final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(id, rpc, + modification, true, rollbackSupported); + try { + twoPhaseCommit.prepare(); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(id + ": Interrupted while waiting for response", e); + } catch (final ExecutionException e) { + logger.warn("%s: Error executing pre commit operation on remote device", id, e); + return new FailingTransaction(twoPhaseCommit, e); + } + + return twoPhaseCommit; + } + + /** + * Always fail commit transaction that rolls back delegate transaction afterwards + */ + private class FailingTransaction implements DataCommitTransaction { + private final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit; + private final ExecutionException e; + + public FailingTransaction(final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit, final ExecutionException e) { + this.twoPhaseCommit = twoPhaseCommit; + this.e = e; + } + + @Override + public DataModification getModification() { + return twoPhaseCommit.getModification(); + } + + @Override + public RpcResult finish() throws IllegalStateException { + return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR, + id + ": Unexpected operation error during pre-commit operations", RpcError.ErrorType.APPLICATION, e)); + } + + @Override + public RpcResult rollback() throws IllegalStateException { + return twoPhaseCommit.rollback(); + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java new file mode 100644 index 0000000000..2909baccdb --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.CONFIG_SOURCE_RUNNING; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure; + +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.common.api.data.DataReader; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; + +public final class NetconfDeviceDataReader implements DataReader { + + private final RpcImplementation rpc; + private final RemoteDeviceId id; + + public NetconfDeviceDataReader(final RemoteDeviceId id, final RpcImplementation rpc) { + this.id = id; + this.rpc = rpc; + } + + @Override + public CompositeNode readConfigurationData(final InstanceIdentifier path) { + final RpcResult result; + try { + result = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME, + NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))).get(); + } catch (final InterruptedException e) { + throw onInterruptedException(e); + } catch (final ExecutionException e) { + throw new RuntimeException(id + ": Read configuration data " + path + " failed", e); + } + + final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); + return data == null ? null : (CompositeNode) findNode(data, path); + } + + private RuntimeException onInterruptedException(final InterruptedException e) { + Thread.currentThread().interrupt(); + return new RuntimeException(id + ": Interrupted while waiting for response", e); + } + + @Override + public CompositeNode readOperationalData(final InstanceIdentifier path) { + final RpcResult result; + try { + result = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path))).get(); + } catch (final InterruptedException e) { + throw onInterruptedException(e); + } catch (final ExecutionException e) { + throw new RuntimeException(id + ": Read operational data " + path + " failed", e); + } + + final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); + return (CompositeNode) findNode(data, path); + } + + private static Node findNode(final CompositeNode node, final InstanceIdentifier identifier) { + + Node current = node; + for (final InstanceIdentifier.PathArgument arg : identifier.getPath()) { + if (current instanceof SimpleNode) { + return null; + } else if (current instanceof CompositeNode) { + final CompositeNode currentComposite = (CompositeNode) current; + + current = currentComposite.getFirstCompositeByName(arg.getNodeType()); + if (current == null) { + current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision()); + } + if (current == null) { + current = currentComposite.getFirstSimpleByName(arg.getNodeType()); + } + if (current == null) { + current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision()); + } + if (current == null) { + return null; + } + } + } + return current; + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java new file mode 100644 index 0000000000..e491496eed --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.FluentIterable; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; +import org.opendaylight.controller.sal.binding.api.data.DataProviderService; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNodeBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device. + * + * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema. + */ +final class NetconfDeviceDatastoreAdapter implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceDatastoreAdapter.class); + + private final RemoteDeviceId id; + private final DataProviderService dataService; + private final ListeningExecutorService executor; + + NetconfDeviceDatastoreAdapter(final RemoteDeviceId deviceId, final DataProviderService dataService, + final ExecutorService executor) { + this.id = Preconditions.checkNotNull(deviceId); + this.dataService = Preconditions.checkNotNull(dataService); + this.executor = MoreExecutors.listeningDecorator(Preconditions.checkNotNull(executor)); + + // Initial data change scheduled + submitDataChangeToExecutor(this.executor, new Runnable() { + @Override + public void run() { + initDeviceData(); + } + }, deviceId); + } + + public void updateDeviceState(final boolean up, final Set capabilities) { + submitDataChangeToExecutor(this.executor, new Runnable() { + @Override + public void run() { + updateDeviceStateInternal(up, capabilities); + } + }, id); + } + + private void updateDeviceStateInternal(final boolean up, final Set capabilities) { + final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node data = buildDataForDeviceState( + up, capabilities, id); + + final DataModificationTransaction transaction = dataService.beginTransaction(); + logger.trace("{}: Update device state transaction {} putting operational data started.", id, transaction.getIdentifier()); + transaction.removeOperationalData(id.getBindingPath()); + transaction.putOperationalData(id.getBindingPath(), data); + logger.trace("{}: Update device state transaction {} putting operational data ended.", id, transaction.getIdentifier()); + + commitTransaction(transaction, "update"); + } + + private void removeDeviceConfigAndState() { + final DataModificationTransaction transaction = dataService.beginTransaction(); + logger.trace("{}: Close device state transaction {} removing all data started.", id, transaction.getIdentifier()); + transaction.removeConfigurationData(id.getBindingPath()); + transaction.removeOperationalData(id.getBindingPath()); + logger.trace("{}: Close device state transaction {} removing all data ended.", id, transaction.getIdentifier()); + + commitTransaction(transaction, "close"); + } + + private void initDeviceData() { + final DataModificationTransaction transaction = dataService.beginTransaction(); + + final InstanceIdentifier path = id.getBindingPath(); + + final Node nodeWithId = getNodeWithId(id); + if (operationalNodeNotExisting(transaction, path)) { + transaction.putOperationalData(path, nodeWithId); + } + if (configurationNodeNotExisting(transaction, path)) { + transaction.putConfigurationData(path, nodeWithId); + } + + commitTransaction(transaction, "init"); + } + + private void commitTransaction(final DataModificationTransaction transaction, final String txType) { + // attempt commit + final RpcResult result; + try { + result = transaction.commit().get(); + } catch (InterruptedException | ExecutionException e) { + logger.error("{}: Transaction({}) failed", id, txType, e); + throw new IllegalStateException(id + " Transaction(" + txType + ") not committed correctly", e); + } + + // verify success result + committed state + if (isUpdateSuccessful(result)) { + logger.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier()); + } else { + logger.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier()); + throw new IllegalStateException(id + " Transaction(" + txType + ") not committed correctly, " + + "Errors: " + result.getErrors()); + } + } + + @Override + public void close() throws Exception { + // Remove device data from datastore + submitDataChangeToExecutor(executor, new Runnable() { + @Override + public void run() { + removeDeviceConfigAndState(); + } + }, id); + } + + private static boolean isUpdateSuccessful(final RpcResult result) { + return result.getResult() == TransactionStatus.COMMITED && result.isSuccessful(); + } + + private static void submitDataChangeToExecutor(final ListeningExecutorService executor, final Runnable r, + final RemoteDeviceId id) { + // Submit data change + final ListenableFuture f = executor.submit(r); + // Verify update execution + Futures.addCallback(f, new FutureCallback() { + @Override + public void onSuccess(final Object result) { + logger.debug("{}: Device data updated successfully", id); + } + + @Override + public void onFailure(final Throwable t) { + logger.warn("{}: Device data update failed", id, t); + } + }); + } + + public static org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node buildDataForDeviceState( + final boolean up, final Set capabilities, final RemoteDeviceId id) { + + final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id); + final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder(); + netconfNodeBuilder.setConnected(up); + netconfNodeBuilder.setInitialCapability(FluentIterable.from(capabilities) + .transform(new Function() { + @Override + public String apply(final QName input) { + return input.toString(); + } + }).toList()); + nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()); + + return nodeBuilder.build(); + } + + private static boolean configurationNodeNotExisting(final DataModificationTransaction transaction, + final InstanceIdentifier path) { + return null == transaction.readConfigurationData(path); + } + + private static boolean operationalNodeNotExisting(final DataModificationTransaction transaction, + final InstanceIdentifier path) { + return null == transaction.readOperationalData(path); + } + + private static Node getNodeWithId(final RemoteDeviceId id) { + final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id); + return nodeBuilder.build(); + } + + private static NodeBuilder getNodeWithIdBuilder(final RemoteDeviceId id) { + final NodeBuilder nodeBuilder = new NodeBuilder(); + nodeBuilder.setKey(id.getBindingKey()); + nodeBuilder.setId(id.getBindingKey().getId()); + return nodeBuilder; + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java new file mode 100644 index 0000000000..927d41861a --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.annotation.Nullable; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connect.api.MessageTransformer; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Invokes RPC by sending netconf message via listener. Also transforms result from NetconfMessage to CompositeNode. + */ +public final class NetconfDeviceRpc implements RpcImplementation { + private final RemoteDeviceCommunicator listener; + private final MessageTransformer transformer; + + public NetconfDeviceRpc(final RemoteDeviceCommunicator listener, final MessageTransformer transformer) { + this.listener = listener; + this.transformer = transformer; + } + + @Override + public Set getSupportedRpcs() { + // TODO is this correct ? + return Collections.emptySet(); + } + + @Override + public ListenableFuture> invokeRpc(final QName rpc, final CompositeNode input) { + final NetconfMessage message = transformRequest(rpc, input); + final ListenableFuture> delegateFutureWithPureResult = listener.sendRequest( + message, rpc); + + + return Futures.transform(delegateFutureWithPureResult, new Function, RpcResult>() { + @Override + public RpcResult apply(@Nullable final RpcResult input) { + return transformResult(input, rpc); + } + }); + } + + private NetconfMessage transformRequest(final QName rpc, final CompositeNode input) { + return transformer.toRpcRequest(rpc, input); + } + + private RpcResult transformResult(final RpcResult netconfMessageRpcResult, + final QName rpc) { + if (netconfMessageRpcResult.isSuccessful()) { + return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc); + } else { + return Rpcs.getRpcResult(false, netconfMessageRpcResult.getErrors()); + } + } + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java new file mode 100644 index 0000000000..37b87045d5 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDeviceHandler { + + private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceSalFacade.class); + private static final InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance(); + + private final RemoteDeviceId id; + private final NetconfDeviceSalProvider salProvider; + + private final List salRegistrations = Lists.newArrayList(); + + public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext, final ExecutorService executor) { + this.id = id; + this.salProvider = new NetconfDeviceSalProvider(id, executor); + registerToSal(domBroker, bindingBroker, bundleContext); + } + + public void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker, final BundleContext bundleContext) { + domRegistryDependency.registerProvider(salProvider, bundleContext); + bindingBroker.registerProvider(salProvider, bundleContext); + } + + @Override + public synchronized void onNotification(final CompositeNode domNotification) { + salProvider.getMountInstance().publish(domNotification); + } + + @Override + public synchronized void onDeviceConnected(final SchemaContextProvider remoteSchemaContextProvider, + final NetconfSessionCapabilities netconfSessionPreferences, final RpcImplementation deviceRpc) { + salProvider.getMountInstance().setSchemaContext(remoteSchemaContextProvider.getSchemaContext()); + salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps()); + registerDataHandlersToSal(deviceRpc, netconfSessionPreferences); + registerRpcsToSal(deviceRpc); + } + + @Override + public void onDeviceDisconnected() { + salProvider.getDatastoreAdapter().updateDeviceState(false, Collections.emptySet()); + } + + private void registerRpcsToSal(final RpcImplementation deviceRpc) { + final MountProvisionInstance mountInstance = salProvider.getMountInstance(); + + final Map failedRpcs = Maps.newHashMap(); + for (final RpcDefinition rpcDef : mountInstance.getSchemaContext().getOperations()) { + try { + salRegistrations.add(mountInstance.addRpcImplementation(rpcDef.getQName(), deviceRpc)); + logger.debug("{}: Rpc {} from netconf registered successfully", id, rpcDef.getQName()); + } catch (final Exception e) { + // Only debug per rpc, warn for all of them at the end to pollute log a little less (e.g. routed rpcs) + logger.debug("{}: Unable to register rpc {} from netconf device. This rpc will not be available", id, + rpcDef.getQName(), e); + failedRpcs.put(rpcDef.getQName(), e.getClass() + ":" + e.getMessage()); + } + } + + if (failedRpcs.isEmpty() == false) { + if (logger.isDebugEnabled()) { + logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs); + } else { + logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs.keySet()); + } + } + } + + private void registerDataHandlersToSal(final RpcImplementation deviceRpc, + final NetconfSessionCapabilities netconfSessionPreferences) { + final NetconfDeviceDataReader dataReader = new NetconfDeviceDataReader(id, deviceRpc); + final NetconfDeviceCommitHandler commitHandler = new NetconfDeviceCommitHandler(id, deviceRpc, + netconfSessionPreferences.isRollbackSupported()); + + final MountProvisionInstance mountInstance = salProvider.getMountInstance(); + salRegistrations.add(mountInstance.registerConfigurationReader(ROOT_PATH, dataReader)); + salRegistrations.add(mountInstance.registerOperationalReader(ROOT_PATH, dataReader)); + salRegistrations.add(mountInstance.registerCommitHandler(ROOT_PATH, commitHandler)); + } + + @Override + public void close() { + for (final AutoCloseable reg : Lists.reverse(salRegistrations)) { + closeGracefully(reg); + } + closeGracefully(salProvider); + } + + private void closeGracefully(final AutoCloseable resource) { + if (resource != null) { + try { + resource.close(); + } catch (final Exception e) { + logger.warn("{}: Ignoring exception while closing {}", id, resource, e); + } + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java new file mode 100644 index 0000000000..01af84c9ac --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import java.util.Collection; +import java.util.Collections; + +import java.util.concurrent.ExecutorService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.binding.api.data.DataProviderService; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider { + + private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class); + + private final RemoteDeviceId id; + private final ExecutorService executor; + private volatile MountProvisionInstance mountInstance; + private volatile NetconfDeviceDatastoreAdapter datastoreAdapter; + + public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final ExecutorService executor) { + this.id = deviceId; + this.executor = executor; + } + + public MountProvisionInstance getMountInstance() { + Preconditions.checkState(mountInstance != null, + "%s: Sal provider was not initialized by sal. Cannot publish notification", id); + return mountInstance; + } + + public NetconfDeviceDatastoreAdapter getDatastoreAdapter() { + Preconditions.checkState(datastoreAdapter != null, + "%s: Sal provider %s was not initialized by sal. Cannot publish notification", id); + return datastoreAdapter; + } + + @Override + public void onSessionInitiated(final Broker.ProviderSession session) { + final MountProvisionService mountService = session.getService(MountProvisionService.class); + if (mountService != null) { + mountInstance = mountService.createOrGetMountPoint(id.getPath()); + } + + logger.debug("{}: (BI)Session with sal established {}", id, session); + } + + @Override + public Collection getProviderFunctionality() { + return Collections.emptySet(); + } + + @Override + public Collection getImplementations() { + return Collections.emptySet(); + } + + @Override + public Collection getFunctionality() { + return Collections.emptySet(); + } + + @Override + public void onSessionInitiated(final BindingAwareBroker.ProviderContext session) { + final DataProviderService dataBroker = session.getSALService(DataProviderService.class); + datastoreAdapter = new NetconfDeviceDatastoreAdapter(id, dataBroker, executor); + + logger.debug("{}: Session with sal established {}", id, session); + } + + @Override + public void onSessionInitialized(final BindingAwareBroker.ConsumerContext session) { + } + + public void close() throws Exception { + mountInstance = null; + datastoreAdapter.close(); + datastoreAdapter = null; + } + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java new file mode 100644 index 0000000000..41f9fec3c4 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.sal; + +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; +import org.opendaylight.controller.md.sal.common.api.data.DataModification; +import org.opendaylight.controller.sal.common.util.RpcErrors; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.FailedRpcResult; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +/** + * Remote transaction that delegates data change to remote device using netconf messages. + */ +final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class); + + private final DataModification modification; + private final RpcImplementation rpc; + private final boolean rollbackSupported; + private final RemoteDeviceId id; + private final CompositeNode targetNode; + + public NetconfDeviceTwoPhaseCommitTransaction(final RemoteDeviceId id, final RpcImplementation rpc, + final DataModification modification, + final boolean candidateSupported, final boolean rollbackOnErrorSupported) { + this.id = id; + this.rpc = Preconditions.checkNotNull(rpc); + this.modification = Preconditions.checkNotNull(modification); + this.targetNode = getTargetNode(candidateSupported); + this.rollbackSupported = rollbackOnErrorSupported; + } + + /** + * Prepare phase, sends 1 or more netconf edit config operations to modify the data + * + * In case of failure or unexpected error response, ExecutionException is thrown + */ + void prepare() throws InterruptedException, ExecutionException { + for (final InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) { + sendDelete(toRemove); + } + for(final Entry toUpdate : modification.getUpdatedConfigurationData().entrySet()) { + sendMerge(toUpdate.getKey(),toUpdate.getValue()); + } + } + + private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException { + sendEditRpc(createEditConfigStructure(key, Optional.absent(), Optional.of(value))); + } + + private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException { + sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.absent())); + } + + private void sendEditRpc(final CompositeNode editStructure) throws InterruptedException, ExecutionException { + final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure); + final RpcResult rpcResult = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, editConfigRequest).get(); + // TODO 874 add default operation when sending delete + + // Check result + if(rpcResult.isSuccessful() == false) { + throw new ExecutionException( + String.format("%s: Pre-commit rpc failed, request: %s, errors: %s", id, editConfigRequest, rpcResult.getErrors()), null); + } + } + + private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure) { + final CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); + + final Node targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.>of(targetNode)); + ret.add(targetWrapperNode); + + if(rollbackSupported) { + ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); + } + ret.setQName(NETCONF_EDIT_CONFIG_QNAME); + ret.add(editStructure); + return ret.toInstance(); + } + + private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional operation, + final Optional lastChildOverride) { + Preconditions.checkArgument(dataPath.getPath().isEmpty() == false, "Instance identifier with empty path %s", dataPath); + + List reversedPath = Lists.reverse(dataPath.getPath()); + + // Create deepest edit element with expected edit operation + CompositeNode previous = getDeepestEditElement(reversedPath.get(0), operation, lastChildOverride); + + // Remove already processed deepest child + reversedPath = Lists.newArrayList(reversedPath); + reversedPath.remove(0); + + // Create edit structure in reversed order + for (final PathArgument arg : reversedPath) { + final CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); + builder.setQName(arg.getNodeType()); + + addPredicatesToCompositeNodeBuilder(getPredicates(arg), builder); + + builder.add(previous); + previous = builder.toInstance(); + } + return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.>of(previous)); + } + + private void addPredicatesToCompositeNodeBuilder(final Map predicates, final CompositeNodeBuilder builder) { + for (final Entry entry : predicates.entrySet()) { + builder.addLeaf(entry.getKey(), entry.getValue()); + } + } + + private Map getPredicates(final PathArgument arg) { + Map predicates = Collections.emptyMap(); + if (arg instanceof NodeIdentifierWithPredicates) { + predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues(); + } + return predicates; + } + + private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional operation, final Optional lastChildOverride) { + final CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); + builder.setQName(arg.getNodeType()); + + final Map predicates = getPredicates(arg); + addPredicatesToCompositeNodeBuilder(predicates, builder); + + if (operation.isPresent()) { + builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get()); + } + if (lastChildOverride.isPresent()) { + final List> children = lastChildOverride.get().getValue(); + for(final Node child : children) { + if(!predicates.containsKey(child.getKey())) { + builder.add(child); + } + } + } + + return builder.toInstance(); + } + + /** + * Send commit rpc to finish the transaction + * In case of failure or unexpected error response, ExecutionException is thrown + */ + @Override + public RpcResult finish() { + try { + final RpcResult rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest()).get(); + return new RpcResultVoidWrapper(rpcResult); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(id + ": Interrupted while waiting for response", e); + } catch (final ExecutionException e) { + LOG.warn("{}: Failed to finish commit operation", id, e); + return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR, + id + ": Unexpected operation error during commit operation", RpcError.ErrorType.APPLICATION, e)); + } + } + + private ImmutableCompositeNode getCommitRequest() { + final CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); + commitInput.setQName(NETCONF_COMMIT_QNAME); + return commitInput.toInstance(); + } + + @Override + public DataModification getModification() { + return this.modification; + } + + @Override + public RpcResult rollback() throws IllegalStateException { + // TODO BUG-732 implement rollback by sending discard changes + return null; + } + + public CompositeNode getTargetNode(final boolean candidateSupported) { + if(candidateSupported) { + return ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.>of()); + } else { + return ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.>of()); + } + } + + private static final class RpcResultVoidWrapper implements RpcResult { + + private final RpcResult rpcResult; + + public RpcResultVoidWrapper(final RpcResult rpcResult) { + this.rpcResult = rpcResult; + } + + @Override + public boolean isSuccessful() { + return rpcResult.isSuccessful(); + } + + @Override + public Void getResult() { + return null; + } + + @Override + public Collection getErrors() { + return rpcResult.getErrors(); + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java new file mode 100644 index 0000000000..9f844fde3f --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.schema; + +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import javax.annotation.concurrent.ThreadSafe; + +import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; +import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +public final class NetconfDeviceSchemaProviderFactory implements SchemaContextProviderFactory { + + private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSchemaProviderFactory.class); + + private final RemoteDeviceId id; + + public NetconfDeviceSchemaProviderFactory(final RemoteDeviceId id) { + this.id = id; + } + + @Override + public SchemaContextProvider createContextProvider(final Collection capabilities, final SchemaSourceProvider sourceProvider) { + + final YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider); + + if (sourceContext.getMissingSources().isEmpty() == false) { + logger.warn("{}: Sources for following models are missing {}", id, sourceContext.getMissingSources()); + } + + logger.debug("{}: Trying to create schema context from {}", id, sourceContext.getValidSources()); + final List modelsToParse = YangSourceContext.getValidInputStreams(sourceContext); + + Preconditions.checkState(sourceContext.getValidSources().isEmpty() == false, + "%s: Unable to create schema context, no sources provided by device", id); + try { + final SchemaContext schemaContext = tryToParseContext(modelsToParse); + logger.debug("{}: Schema context successfully created.", id); + return new NetconfSchemaContextProvider(schemaContext); + } catch (final RuntimeException e) { + logger.error("{}: Unable to create schema context, unexpected error", id, e); + throw new IllegalStateException(id + ": Unable to create schema context", e); + } + } + + private static SchemaContext tryToParseContext(final List modelsToParse) { + final YangParserImpl parser = new YangParserImpl(); + final Set models = parser.parseYangModelsFromStreams(modelsToParse); + return parser.resolveSchemaContext(models); + } + + private static final class NetconfSchemaContextProvider implements SchemaContextProvider { + private final SchemaContext schemaContext; + + public NetconfSchemaContextProvider(final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + } + + @Override + public SchemaContext getSchemaContext() { + return schemaContext; + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java new file mode 100644 index 0000000000..44ff2ef985 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.schema; + +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; + +public final class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider { + + public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, + "get-schema"); + public static final QName GET_DATA_QNAME = QName + .create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data"); + + private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaSourceProvider.class); + + private final RpcImplementation rpc; + private final RemoteDeviceId id; + + public NetconfRemoteSchemaSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) { + this.id = id; + this.rpc = Preconditions.checkNotNull(rpc); + } + + @Override + public Optional getSchemaSource(final String moduleName, final Optional revision) { + final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision); + + logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision); + try { + final RpcResult schemaReply = rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest).get(); + if (schemaReply.isSuccessful()) { + final Optional schemaBody = getSchemaFromRpc(id, schemaReply.getResult()); + if (schemaBody.isPresent()) { + logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision); + return schemaBody; + } + } else { + logger.warn("{}: YANG schema was not successfully retrieved for {}:{}. Errors: {}", id, moduleName, + revision, schemaReply.getErrors()); + } + return Optional.absent(); + } catch (final InterruptedException e){ + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } catch (final Exception e) { + logger.error("{}: YANG schema was not successfully retrieved for {}:{}", id, moduleName, revision, e); + throw new IllegalStateException(e); + } + } + + private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional revision) { + final CompositeNodeBuilder request = ImmutableCompositeNode.builder(); + request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName); + if (revision.isPresent()) { + request.addLeaf("version", revision.get()); + } + request.addLeaf("format", "yang"); + return request.toInstance(); + } + + private static Optional getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) { + if (result == null) { + return Optional.absent(); + } + final SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); + + Preconditions.checkNotNull(simpleNode, + "%s Unexpected response to get-schema, expected response with one child %s, but was %s", + id, GET_DATA_QNAME.withoutRevision(), result); + + final Object potential = simpleNode.getValue(); + return potential instanceof String ? Optional.of((String) potential) : Optional.absent(); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java new file mode 100644 index 0000000000..c85a52909d --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf.schema.mapping; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.activation.UnsupportedDataTypeException; + +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connect.api.MessageTransformer; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.MessageCounter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.google.common.base.Optional; + +public class NetconfMessageTransformer implements MessageTransformer { + + public static final String MESSAGE_ID_PREFIX = "m"; + + private Optional schemaContext = Optional.absent(); + private final MessageCounter counter; + + public NetconfMessageTransformer() { + this.counter = new MessageCounter(); + } + + @Override + public synchronized CompositeNode toNotification(final NetconfMessage message) { + if(schemaContext.isPresent()) { + return toNotification(message, schemaContext.get()); + } else { + return XmlDocumentUtils.notificationToDomNodes(message.getDocument(), Optional.>absent()); + } + } + + private static CompositeNode toNotification(final NetconfMessage message, final SchemaContext ctx) { + final Set notifications = ctx.getNotifications(); + final Document document = message.getDocument(); + return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications)); + } + + @Override + public NetconfMessage toRpcRequest(final QName rpc, final CompositeNode node) { + final CompositeNodeTOImpl rpcPayload = NetconfMessageTransformUtil.wrap( + NetconfMessageTransformUtil.NETCONF_RPC_QNAME, NetconfMessageTransformUtil.flattenInput(node)); + final Document w3cPayload; + try { + w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider()); + } catch (final UnsupportedDataTypeException e) { + throw new IllegalArgumentException("Unable to create message", e); + } + w3cPayload.getDocumentElement().setAttribute("message-id", counter.getNewMessageId(MESSAGE_ID_PREFIX)); + return new NetconfMessage(w3cPayload); + } + + @Override + public synchronized RpcResult toRpcResult(final NetconfMessage message, final QName rpc) { + if(schemaContext.isPresent()) { + return toRpcResult(message, rpc, schemaContext.get()); + } else { + final CompositeNode node = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument()); + return Rpcs.getRpcResult(true, node, Collections.emptySet()); + } + } + + private static RpcResult toRpcResult(final NetconfMessage message, final QName rpc, final SchemaContext context) { + final CompositeNode compositeNode; + + if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) { + + final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument()); + + final List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, + Optional.of(context.getDataDefinitions()), context); + + final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); + it.setQName(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME); + it.add(ImmutableCompositeNode.create(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataNodes)); + + compositeNode = it.toInstance(); + } else { + // TODO map rpc with schema + compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument()); + } + + return Rpcs.getRpcResult(true, compositeNode, Collections. emptySet()); + } + + @Override + public synchronized void onGlobalContextUpdated(final SchemaContext schemaContext) { + this.schemaContext = Optional.of(schemaContext); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java similarity index 50% rename from opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java rename to opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index 2b3a992fc1..0c6ad45fbb 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -5,103 +5,79 @@ * 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.controller.sal.connect.netconf; +package org.opendaylight.controller.sal.connect.netconf.util; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import javax.activation.UnsupportedDataTypeException; +import java.util.Map; import javax.annotation.Nullable; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; -import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.w3c.dom.Document; import org.w3c.dom.Element; -import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -public class NetconfMapping { +public class NetconfMessageTransformUtil { - public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"); - public static String NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"; - public static URI NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0"); - public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0"); + private NetconfMessageTransformUtil() { + } + public static final QName IETF_NETCONF_MONITORING = QName.create( + "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring"); + public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"); public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf"); - public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc"); - public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get"); - public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter"); - public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type"); - public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config"); - public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config"); - public static QName NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config"); - public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation"); - public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit"); - - public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config"); - public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source"); - public static QName NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target"); - - public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate"); - public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running"); - - public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option"); - public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error"; - - public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply"); - public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok"); public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data"); - public static QName NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null, - "create-subscription"); - public static QName NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null, - "cancel-subscription"); - public static QName IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04", - "ietf-netconf-monitoring"); - - static List> RUNNING = Collections.> singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME, + public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply"); + public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option"); + public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running"); + static List> RUNNING = Collections.> singletonList(new SimpleNodeTOImpl<>(NETCONF_RUNNING_QNAME, null, null)); - + public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source"); public static CompositeNode CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING); + public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate"); + public static QName NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target"); + public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config"); + public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit"); + public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation"); + public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config"); + public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config"); + public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type"); + public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter"); + public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get"); + public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc"); + public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI + .create("urn:ietf:params:netconf:capability:rollback-on-error:1.0"); + public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error"; - static AtomicInteger messageId = new AtomicInteger(0); - - static Node toFilterStructure(final InstanceIdentifier identifier) { + public static Node toFilterStructure(final InstanceIdentifier identifier) { Node previous = null; if (identifier.getPath().isEmpty()) { return null; } - for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists + for (final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists .reverse(identifier.getPath())) { - if (component instanceof NodeIdentifierWithPredicates) { - previous = toNode((NodeIdentifierWithPredicates)component, previous); + if (component instanceof InstanceIdentifier.NodeIdentifierWithPredicates) { + previous = toNode((InstanceIdentifier.NodeIdentifierWithPredicates)component, previous); } else { previous = toNode(component, previous); } @@ -109,9 +85,9 @@ public class NetconfMapping { return filter("subtree", previous); } - static Node toNode(final NodeIdentifierWithPredicates argument, final Node node) { - List> list = new ArrayList<>(); - for (Map.Entry arg : argument.getKeyValues().entrySet()) { + static Node toNode(final InstanceIdentifier.NodeIdentifierWithPredicates argument, final Node node) { + final List> list = new ArrayList<>(); + for (final Map.Entry arg : argument.getKeyValues().entrySet()) { list.add(new SimpleNodeTOImpl(arg.getKey(), null, arg.getValue())); } if (node != null) { @@ -120,51 +96,31 @@ public class NetconfMapping { return new CompositeNodeTOImpl(argument.getNodeType(), null, list); } - static Node toNode(final PathArgument argument, final Node node) { - if (node != null) { - return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.> singletonList(node)); - } else { - return new SimpleNodeTOImpl(argument.getNodeType(), null, null); - } - } - - static CompositeNode toCompositeNode(final NetconfMessage message, final Optional ctx) { - // TODO: implement general normalization to normalize incoming Netconf - // Message - // for Schema Context counterpart - return null; - } + public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) { + final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id"); + final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id"); - static CompositeNode toNotificationNode(final NetconfMessage message, final Optional ctx) { - if (ctx.isPresent()) { - SchemaContext schemaContext = ctx.get(); - Set notifications = schemaContext.getNotifications(); - Document document = message.getDocument(); - return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications), ctx.get()); + if(inputMsgId.equals(outputMsgId) == false) { + final String requestXml = XmlUtil.toString(input.getDocument()); + final String responseXml = XmlUtil.toString(output.getDocument()); + throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml)); } - return null; } - static NetconfMessage toRpcMessage(final QName rpc, final CompositeNode node, final Optional ctx) { - CompositeNodeTOImpl rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node)); - Document w3cPayload = null; - try { - w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider()); - } catch (UnsupportedDataTypeException e) { - throw new IllegalArgumentException("Unable to create message", e); + public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException { + if(NetconfMessageUtil.isErrorMessage(output)) { + throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument()))); } - w3cPayload.getDocumentElement().setAttribute("message-id", "m-" + messageId.getAndIncrement()); - return new NetconfMessage(w3cPayload); } - static CompositeNode flattenInput(final CompositeNode node) { + public static CompositeNode flattenInput(final CompositeNode node) { final QName inputQName = QName.create(node.getNodeType(), "input"); - CompositeNode input = node.getFirstCompositeByName(inputQName); + final CompositeNode input = node.getFirstCompositeByName(inputQName); if (input == null) return node; if (input instanceof CompositeNode) { - List> nodes = ImmutableList.> builder() // + final List> nodes = ImmutableList.> builder() // .addAll(input.getValue()) // .addAll(Collections2.filter(node.getValue(), new Predicate>() { @Override @@ -180,43 +136,25 @@ public class NetconfMapping { return input; } - static RpcResult toRpcResult(final NetconfMessage message, final QName rpc, final Optional context) { - CompositeNode rawRpc; - if (context.isPresent()) - if (isDataRetrieQNameReply(rpc)) { - - Element xmlData = getDataSubtree(message.getDocument()); - - List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, - Optional.of(context.get().getDataDefinitions())); - - CompositeNodeBuilder it = ImmutableCompositeNode.builder(); - it.setQName(NETCONF_RPC_REPLY_QNAME); - it.add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME, dataNodes)); - - rawRpc = it.toInstance(); - // sys(xmlData) - } else { - rawRpc = toCompositeNode(message, context); - } - else { - rawRpc = (CompositeNode) toCompositeNode(message.getDocument()); + static Node toNode(final InstanceIdentifier.PathArgument argument, final Node node) { + if (node != null) { + return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.> singletonList(node)); + } else { + return new SimpleNodeTOImpl(argument.getNodeType(), null, null); } - // rawRpc. - return Rpcs.getRpcResult(true, rawRpc, Collections. emptySet()); } - static Element getDataSubtree(final Document doc) { + public static Element getDataSubtree(final Document doc) { return (Element) doc.getElementsByTagNameNS(NETCONF_URI.toString(), "data").item(0); } - static boolean isDataRetrieQNameReply(final QName it) { - return NETCONF_URI == it.getNamespace() - && (it.getLocalName() == NETCONF_GET_CONFIG_QNAME.getLocalName() || it.getLocalName() == NETCONF_GET_QNAME - .getLocalName()); + public static boolean isDataRetrievalOperation(final QName rpc) { + return NETCONF_URI == rpc.getNamespace() + && (rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName()) || rpc.getLocalName().equals( + NETCONF_GET_QNAME.getLocalName())); } - static CompositeNodeTOImpl wrap(final QName name, final Node node) { + public static CompositeNodeTOImpl wrap(final QName name, final Node node) { if (node != null) { return new CompositeNodeTOImpl(name, null, Collections.> singletonList(node)); } else { @@ -224,7 +162,7 @@ public class NetconfMapping { } } - static CompositeNodeTOImpl wrap(final QName name, final Node additional, final Node node) { + public static CompositeNodeTOImpl wrap(final QName name, final Node additional, final Node node) { if (node != null) { return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node)); } else { @@ -233,7 +171,7 @@ public class NetconfMapping { } static ImmutableCompositeNode filter(final String type, final Node node) { - CompositeNodeBuilder it = ImmutableCompositeNode.builder(); // + final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); // it.setQName(NETCONF_FILTER_QNAME); it.setAttribute(NETCONF_TYPE_QNAME, type); if (node != null) { @@ -243,24 +181,4 @@ public class NetconfMapping { } } - public static Node toCompositeNode(final Document document) { - return XmlDocumentUtils.toDomNode(document); - } - - public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) { - String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id"); - String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id"); - - if(inputMsgId.equals(outputMsgId) == false) { - String requestXml = XmlUtil.toString(input.getDocument()); - String responseXml = XmlUtil.toString(output.getDocument()); - throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml)); - } - } - - public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException { - if(NetconfMessageUtil.isErrorMessage(output)) { - throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument()))); - } - } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java new file mode 100644 index 0000000000..49b16d4cfb --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.util; + +import java.util.Collection; +import java.util.Collections; + +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public final class FailedRpcResult implements RpcResult { + + private final RpcError rpcError; + + public FailedRpcResult(final RpcError rpcError) { + this.rpcError = rpcError; + } + + @Override + public boolean isSuccessful() { + return false; + } + + @Override + public T getResult() { + return null; + } + + @Override + public Collection getErrors() { + return Collections.singletonList(rpcError); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java new file mode 100644 index 0000000000..2b2f6a9253 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java @@ -0,0 +1,21 @@ +package org.opendaylight.controller.sal.connect.util; + +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +public class MessageCounter { + final AtomicInteger messageId = new AtomicInteger(0); + + private static final String messageIdBlueprint = "%s-%s"; + + public String getNewMessageId(final String prefix) { + Preconditions.checkArgument(Strings.isNullOrEmpty(prefix) == false, "Null or empty prefix"); + return String.format(messageIdBlueprint, prefix, getNewMessageId()); + } + + public String getNewMessageId() { + return Integer.toString(messageId.getAndIncrement()); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java new file mode 100644 index 0000000000..4670846c7c --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.util; + +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.base.Preconditions; +import org.opendaylight.yangtools.yang.common.QName; + +public class RemoteDeviceId { + + private final String name; + private final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path; + private final InstanceIdentifier bindingPath; + private final NodeKey key; + + public RemoteDeviceId(final ModuleIdentifier identifier) { + this(Preconditions.checkNotNull(identifier).getInstanceName()); + } + + public RemoteDeviceId(final String name) { + Preconditions.checkNotNull(name); + this.name = name; + this.key = new NodeKey(new NodeId(name)); + this.path = createBIPath(name); + this.bindingPath = createBindingPath(key); + } + + private static InstanceIdentifier createBindingPath(final NodeKey key) { + return InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build(); + } + + private static org.opendaylight.yangtools.yang.data.api.InstanceIdentifier createBIPath(final String name) { + final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder builder = + org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder(); + builder.node(Nodes.QNAME).nodeWithKey(Node.QNAME, QName.create(Node.QNAME.getNamespace(), Node.QNAME.getRevision(), "id"), name); + + return builder.build(); + } + + public String getName() { + return name; + } + + public InstanceIdentifier getBindingPath() { + return bindingPath; + } + + public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getPath() { + return path; + } + + public NodeKey getBindingKey() { + return key; + } + + @Override + public String toString() { + return "RemoteDevice{" + name +'}'; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof RemoteDeviceId)) return false; + + final RemoteDeviceId that = (RemoteDeviceId) o; + + if (!name.equals(that.name)) return false; + if (!bindingPath.equals(that.bindingPath)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + bindingPath.hashCode(); + return result; + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java new file mode 100644 index 0000000000..21b9d3a436 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java @@ -0,0 +1,6 @@ +/** + * Utility classes for remote connectors e.g. netconf connector + * + * TODO extract into separate bundle when another connector is implemented e.g. restconf connector + */ +package org.opendaylight.controller.sal.connect.util; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang index d4dad11ec3..c59c41c437 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang @@ -3,10 +3,11 @@ module odl-sal-netconf-connector-cfg { namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf"; prefix "sal-netconf"; - import config { prefix config; revision-date 2013-04-05; } - import threadpool {prefix th;} - import netty {prefix netty;} - import opendaylight-md-sal-dom {prefix dom;} + import config { prefix config; revision-date 2013-04-05; } + import threadpool {prefix th;} + import netty {prefix netty;} + import opendaylight-md-sal-dom {prefix dom;} + import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;} import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; } description @@ -22,7 +23,6 @@ module odl-sal-netconf-connector-cfg { config:java-name-prefix NetconfConnector; } - grouping server { leaf address { type string; @@ -33,7 +33,6 @@ module odl-sal-netconf-connector-cfg { } } - augment "/config:modules/config:module/config:configuration" { case sal-netconf-connector { when "/config:modules/config:module/config:type = 'sal-netconf-connector'"; @@ -67,6 +66,16 @@ module odl-sal-netconf-connector-cfg { } } + container binding-registry { + uses config:service-ref { + refine type { + // FIXME BUG-944 make mandatory (remove backwards compatibility) + mandatory false; + config:required-identity md-sal-binding:binding-broker-osgi-registry; + } + } + } + // FIXME BUG-944 remove backwards compatibility // Deprecated, replaced by client dispatcher. // This dependency will be removed in near future and all configurations of netconf-connector need to be changed to use dispatcher dependency. @@ -99,6 +108,18 @@ module odl-sal-netconf-connector-cfg { } } + container processing-executor { + uses config:service-ref { + refine type { + // FIXME BUG-944 make mandatory (remove backwards compatibility) + mandatory false; + config:required-identity th:threadpool; + } + } + + description "Makes up for flaws in netty threading design"; + } + // Replaces thread group dependencies container client-dispatcher { uses config:service-ref { @@ -122,9 +143,16 @@ module odl-sal-netconf-connector-cfg { } leaf between-attempts-timeout-millis { - description "Timeout in milliseconds to wait between connection attempts."; + description "Initial timeout in milliseconds to wait between connection attempts. Will be multiplied by sleep-factor with every additional attempt"; type uint16; - default 10000; + default 2000; + } + + leaf sleep-factor { + type decimal64 { + fraction-digits 1; + } + default 1.5; } } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java new file mode 100644 index 0000000000..5ac32b5b3c --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.sal.connect.netconf; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.connect.api.MessageTransformer; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; +import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.Futures; + +public class NetconfDeviceTest { + + private static final NetconfMessage netconfMessage; + private static final CompositeNode compositeNode; + + static { + try { + netconfMessage = mockClass(NetconfMessage.class); + compositeNode = mockClass(CompositeNode.class); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private static final RpcResult rpcResult = Rpcs.getRpcResult(true, netconfMessage, Collections.emptySet()); + private static final RpcResult rpcResultC = Rpcs.getRpcResult(true, compositeNode, Collections.emptySet()); + + public static final String TEST_NAMESPACE = "test:namespace"; + public static final String TEST_MODULE = "test-module"; + public static final String TEST_REVISION = "2013-07-22"; + + @Test + public void testNetconfDeviceWithoutMonitoring() throws Exception { + final RemoteDeviceHandler facade = getFacade(); + final RemoteDeviceCommunicator listener = getListener(); + + final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), getMessageTransformer(), getSchemaContextProviderFactory(), getSourceProviderFactory()); + device.onRemoteSessionUp(getSessionCaps(false, Collections.emptyList()), listener); + + Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected(); + } + + @Test + public void testNetconfDeviceReconnect() throws Exception { + final RemoteDeviceHandler facade = getFacade(); + final RemoteDeviceCommunicator listener = getListener(); + + final SchemaContextProviderFactory schemaContextProviderFactory = getSchemaContextProviderFactory(); + final SchemaSourceProviderFactory sourceProviderFactory = getSourceProviderFactory(); + final MessageTransformer messageTransformer = getMessageTransformer(); + + final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, schemaContextProviderFactory, sourceProviderFactory); + final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, + Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION)); + device.onRemoteSessionUp(sessionCaps, listener); + + verify(sourceProviderFactory, timeout(5000)).createSourceProvider(any(RpcImplementation.class)); + verify(schemaContextProviderFactory, timeout(5000)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); + verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class)); + verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + + device.onRemoteSessionDown(); + verify(facade, timeout(5000)).onDeviceDisconnected(); + + device.onRemoteSessionUp(sessionCaps, listener); + + verify(sourceProviderFactory, timeout(5000).times(2)).createSourceProvider(any(RpcImplementation.class)); + verify(schemaContextProviderFactory, timeout(5000).times(2)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); + verify(messageTransformer, timeout(5000).times(2)).onGlobalContextUpdated(any(SchemaContext.class)); + verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + } + + private SchemaContextProviderFactory getSchemaContextProviderFactory() { + final SchemaContextProviderFactory schemaContextProviderFactory = mockClass(SchemaContextProviderFactory.class); + doReturn(new SchemaContextProvider() { + @Override + public SchemaContext getSchemaContext() { + return getSchema(); + } + }).when(schemaContextProviderFactory).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); + return schemaContextProviderFactory; + } + + public static SchemaContext getSchema() { + final YangParserImpl parser = new YangParserImpl(); + final List modelsToParse = Lists.newArrayList( + NetconfDeviceTest.class.getResourceAsStream("/schemas/test-module.yang") + ); + final Set models = parser.parseYangModelsFromStreams(modelsToParse); + return parser.resolveSchemaContext(models); + } + + private RemoteDeviceHandler getFacade() throws Exception { + final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class); + doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + doNothing().when(remoteDeviceHandler).onDeviceDisconnected(); + return remoteDeviceHandler; + } + + private T mockCloseableClass(final Class remoteDeviceHandlerClass) throws Exception { + final T mock = mockClass(remoteDeviceHandlerClass); + doNothing().when(mock).close(); + return mock; + } + + public SchemaSourceProviderFactory getSourceProviderFactory() { + final SchemaSourceProviderFactory mock = mockClass(SchemaSourceProviderFactory.class); + + final SchemaSourceProvider schemaSourceProvider = mockClass(SchemaSourceProvider.class); + doReturn(Optional.absent()).when(schemaSourceProvider).getSchemaSource(anyString(), any(Optional.class)); + + doReturn(schemaSourceProvider).when(mock).createSourceProvider(any(RpcImplementation.class)); + return mock; + } + + private static T mockClass(final Class remoteDeviceHandlerClass) { + final T mock = Mockito.mock(remoteDeviceHandlerClass); + Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString(); + return mock; + } + + public RemoteDeviceId getId() { + return new RemoteDeviceId("test-D"); + } + + public ExecutorService getExecutor() { + return Executors.newSingleThreadExecutor(); + } + + public MessageTransformer getMessageTransformer() throws Exception { + final MessageTransformer messageTransformer = mockClass(MessageTransformer.class); + doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class)); + doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class)); + doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class)); + return messageTransformer; + } + + public NetconfSessionCapabilities getSessionCaps(final boolean addMonitor, final Collection additionalCapabilities) { + final ArrayList capabilities = Lists.newArrayList( + XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, + XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1); + + if(addMonitor) { + capabilities.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString()); + } + + capabilities.addAll(additionalCapabilities); + + return NetconfSessionCapabilities.fromStrings( + capabilities); + } + + public RemoteDeviceCommunicator getListener() throws Exception { + final RemoteDeviceCommunicator remoteDeviceCommunicator = mockCloseableClass(RemoteDeviceCommunicator.class); + doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class)); + return remoteDeviceCommunicator; + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang new file mode 100644 index 0000000000..cd732fca70 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang @@ -0,0 +1,18 @@ +module test-module { + yang-version 1; + namespace "test:namespace"; + prefix "tt"; + + description + "Types for testing"; + + revision "2013-07-22"; + + + container c { + leaf a { + type string; + } + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java index 593c104dfd..1f7b061e92 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java @@ -31,6 +31,12 @@ class JsonReader { JsonParser parser = new JsonParser(); JsonElement rootElement = parser.parse(new InputStreamReader(entityStream)); + if( rootElement.isJsonNull() ) + { + //no content, so return null to indicate no input + return null; + } + if (!rootElement.isJsonObject()) { throw new UnsupportedFormatException("Root element of Json has to be Object"); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java index 5b95f0de1a..171805a179 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java @@ -9,6 +9,8 @@ package org.opendaylight.controller.sal.rest.impl; import static com.google.common.base.Preconditions.checkArgument; +import java.io.BufferedInputStream; +import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Stack; @@ -32,7 +34,17 @@ public class XmlReader { private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); private XMLEventReader eventReader; - public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException { + public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, + UnsupportedFormatException, + IOException { + //Get an XML stream which can be marked, and reset, so we can check and see if there is + //any content being provided. + entityStream = getMarkableStream(entityStream); + + if( isInputStreamEmpty( entityStream ) ) { + return null; + } + eventReader = xmlInputFactory.createXMLEventReader(entityStream); if (eventReader.hasNext()) { @@ -91,6 +103,31 @@ public class XmlReader { return root; } + /** + * If the input stream is not markable, then it wraps the input stream with a buffered stream, + * which is mark able. That way we can check if the stream is empty safely. + * @param entityStream + * @return + */ + private InputStream getMarkableStream(InputStream entityStream) { + if( !entityStream.markSupported() ) + { + entityStream = new BufferedInputStream( entityStream ); + } + return entityStream; + } + + private boolean isInputStreamEmpty(InputStream entityStream) + throws IOException { + boolean isEmpty = false; + entityStream.mark( 1 ); + if( entityStream.read() == -1 ){ + isEmpty = true; + } + entityStream.reset(); + return isEmpty; + } + private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException { checkArgument(event != null, "XML Event cannot be NULL!"); if (event.isStartElement()) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index ad682bc829..c0ce90e15d 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -8,13 +8,6 @@ */ package org.opendaylight.controller.sal.restconf.impl; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -38,21 +31,11 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.rest.api.Draft02; import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor; import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor; import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor; -import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; -import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.ControllerContext; -import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; -import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.RestCodec; -import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.StructuredData; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter; import org.opendaylight.controller.sal.streams.listeners.Notificator; import org.opendaylight.controller.sal.streams.websockets.WebSocketServer; @@ -84,6 +67,13 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType; import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder; import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + public class RestconfImpl implements RestconfService { private final static RestconfImpl INSTANCE = new RestconfImpl(); @@ -401,13 +391,36 @@ public class RestconfImpl implements RestconfService { URI rpcNamespace = rpcName.getNamespace(); if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) && Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) { - return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition()); } + validateInput( rpc.getRpcDefinition().getInput(), payload ); + return callRpc(rpc, payload); } + private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) { + if( inputSchema != null && payload == null ) + { + //expected a non null payload + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + else if( inputSchema == null && payload != null ) + { + //did not expect any input + throw new RestconfDocumentedException( "No input expected.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + //else + //{ + //TODO: Validate "mandatory" and "config" values here??? Or should those be + // validate in a more central location inside MD-SAL core. + //} + } + private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload, final RpcDefinition rpc) { final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null); @@ -455,8 +468,7 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException( "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } - final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier); - return callRpc(rpc, null); + return invokeRpc( identifier, (CompositeNode)null ); } private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) { @@ -586,6 +598,9 @@ public class RestconfImpl implements RestconfService { @Override public Response updateConfigurationData(final String identifier, final CompositeNode payload) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + + validateInput(iiWithData.getSchemaNode(), payload); + MountInstance mountPoint = iiWithData.getMountPoint(); final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint); RpcResult status = null; @@ -610,6 +625,12 @@ public class RestconfImpl implements RestconfService { @Override public Response createConfigurationData(final String identifier, final CompositeNode payload) { + if( payload == null ) { + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + URI payloadNS = this.namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( @@ -685,6 +706,12 @@ public class RestconfImpl implements RestconfService { @Override public Response createConfigurationData(final CompositeNode payload) { + if( payload == null ) { + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + URI payloadNS = this.namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java index 312365585e..4e32e7058c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java @@ -11,49 +11,127 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.ws.rs.WebApplicationException; +import java.util.Map; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider; import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import com.google.common.collect.Maps; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader { + static abstract class LeafVerifier { + + Object expectedValue; + JsonToken expectedToken; + + LeafVerifier( Object expectedValue, JsonToken expectedToken ) { + this.expectedValue = expectedValue; + this.expectedToken = expectedToken; + } + + abstract Object getActualValue( JsonReader reader ) throws IOException; + + void verify( JsonReader reader, String keyName ) throws IOException { + assertEquals( "Json value for key " + keyName, expectedValue, getActualValue( reader ) ); + } + + JsonToken expectedTokenType() { + return expectedToken; + } + } + + static class BooleanVerifier extends LeafVerifier { + + public BooleanVerifier( boolean expected ) { + super( expected, JsonToken.BOOLEAN ); + } + + @Override + Object getActualValue( JsonReader reader ) throws IOException { + return reader.nextBoolean(); + } + } + + static class NumberVerifier extends LeafVerifier { + + public NumberVerifier( Number expected ) { + super( expected, JsonToken.NUMBER ); + } + + @Override + Object getActualValue( JsonReader reader ) throws IOException { + if( expectedValue instanceof Double ) { + return reader.nextDouble(); + } + else if( expectedValue instanceof Long ) { + return reader.nextLong(); + } + else if( expectedValue instanceof Integer ) { + return reader.nextInt(); + } + + return null; + } + } + + static class StringVerifier extends LeafVerifier { + + StringVerifier( String expected ) { + super( expected, JsonToken.STRING ); + } + + @Override + Object getActualValue( JsonReader reader ) throws IOException { + return reader.nextString(); + } + } + + static class EmptyVerifier extends LeafVerifier { + + EmptyVerifier() { + super( null, null ); + } + + @Override + Object getActualValue( JsonReader reader ) throws IOException { + reader.beginArray(); + reader.nextNull(); + reader.endArray(); + return null; + } + + } + @BeforeClass public static void initialize() { dataLoad("/cnsn-to-json/simple-data-types"); } @Test - public void simpleYangDataTest() { + public void simpleYangDataTest() throws Exception { CompositeNode compositeNode = TestUtils.readInputToCnSn("/cnsn-to-json/simple-data-types/xml/data.xml", XmlToCompositeNodeProvider.INSTANCE); - String jsonOutput = null; - TestUtils.normalizeCompositeNode(compositeNode, modules, "simple-data-types:cont"); - try { - jsonOutput = TestUtils.writeCompNodeWithSchemaContextToOutput(compositeNode, modules, dataSchemaNode, + String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToOutput(compositeNode, modules, dataSchemaNode, StructuredDataToJsonProvider.INSTANCE); - } catch (WebApplicationException | IOException e) { - } + assertNotNull(jsonOutput); verifyJsonOutput(jsonOutput); @@ -88,167 +166,84 @@ public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader private void jsonReadContElements(JsonReader jReader) throws IOException { jReader.beginObject(); - List loadedLfs = new ArrayList<>(); - boolean enumChecked = false; - boolean bitsChecked = false; - boolean lfdecimal6Checked = false; - boolean lfdecimal4Checked = false; - boolean lfdecimal3Checked = false; - boolean lfdecimal2Checked = false; - boolean lfdecimal1Checked = false; - boolean lfbool1Checked = false; - boolean lfbool2Checked = false; - boolean lfstrChecked = false; - boolean lfbinaryChecked = false; - boolean lfemptyChecked = false; - boolean lfstr1Checked = false; - boolean lfidentityrefChecked = false; + + Map expectedMap = Maps.newHashMap(); + expectedMap.put( "lfnint8Min", new NumberVerifier( Integer.valueOf( -128 ) ) ); + expectedMap.put( "lfnint8Max", new NumberVerifier( Integer.valueOf( 127 ) ) ); + expectedMap.put( "lfnint16Min", new NumberVerifier( Integer.valueOf( -32768 ) ) ); + expectedMap.put( "lfnint16Max", new NumberVerifier( Integer.valueOf( 32767 ) ) ); + expectedMap.put( "lfnint32Min", new NumberVerifier( Integer.valueOf( -2147483648 ) ) ); + expectedMap.put( "lfnint32Max", new NumberVerifier( Long.valueOf( 2147483647 ) ) ); + expectedMap.put( "lfnint64Min", new NumberVerifier( Long.valueOf( -9223372036854775808L ) ) ); + expectedMap.put( "lfnint64Max", new NumberVerifier( Long.valueOf( 9223372036854775807L ) ) ); + expectedMap.put( "lfnuint8Max", new NumberVerifier( Integer.valueOf( 255 ) ) ); + expectedMap.put( "lfnuint16Max", new NumberVerifier( Integer.valueOf( 65535 ) ) ); + expectedMap.put( "lfnuint32Max", new NumberVerifier( Long.valueOf( 4294967295L ) ) ); + expectedMap.put( "lfstr", new StringVerifier( "lfstr" ) ); + expectedMap.put( "lfstr1", new StringVerifier( "" ) ); + expectedMap.put( "lfbool1", new BooleanVerifier( true ) ); + expectedMap.put( "lfbool2", new BooleanVerifier( false ) ); + expectedMap.put( "lfbool3", new BooleanVerifier( false ) ); + expectedMap.put( "lfdecimal1", new NumberVerifier( new Double( 43.32 ) ) ); + expectedMap.put( "lfdecimal2", new NumberVerifier( new Double( -0.43 ) ) ); + expectedMap.put( "lfdecimal3", new NumberVerifier( new Double( 43 ) ) ); + expectedMap.put( "lfdecimal4", new NumberVerifier( new Double( 43E3 ) ) ); + expectedMap.put( "lfdecimal6", new NumberVerifier( new Double( 33.12345 ) ) ); + expectedMap.put( "lfenum", new StringVerifier( "enum3" ) ); + expectedMap.put( "lfbits", new StringVerifier( "bit3 bit2" ) ); + expectedMap.put( "lfbinary", new StringVerifier( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ) ); + expectedMap.put( "lfunion1", new StringVerifier( "324" ) ); + expectedMap.put( "lfunion2", new StringVerifier( "33.3" ) ); + expectedMap.put( "lfunion3", new StringVerifier( "55" ) ); + expectedMap.put( "lfunion4", new StringVerifier( "true" ) ); + expectedMap.put( "lfunion5", new StringVerifier( "true" ) ); + expectedMap.put( "lfunion6", new StringVerifier( "10" ) ); + expectedMap.put( "lfunion7", new StringVerifier( "" ) ); + expectedMap.put( "lfunion8", new StringVerifier( "" ) ); + expectedMap.put( "lfunion9", new StringVerifier( "" ) ); + expectedMap.put( "lfunion10", new StringVerifier( "bt1" ) ); + expectedMap.put( "lfunion11", new StringVerifier( "33" ) ); + expectedMap.put( "lfunion12", new StringVerifier( "false" ) ); + expectedMap.put( "lfunion13", new StringVerifier( "b1" ) ); + expectedMap.put( "lfunion14", new StringVerifier( "zero" ) ); + expectedMap.put( "lfempty", new EmptyVerifier() ); + expectedMap.put( "identityref1", new StringVerifier( "simple-data-types:iden" ) ); while (jReader.hasNext()) { String keyName = jReader.nextName(); - JsonToken peek = null; - try { - peek = jReader.peek(); - } catch (IOException e) { - assertTrue("Key " + keyName + " has incorrect value for specifed type", false); - } + JsonToken peek = jReader.peek(); - if (keyName.startsWith("lfnint") || keyName.startsWith("lfnuint")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - try { - jReader.nextLong(); - } catch (NumberFormatException e) { - assertTrue("Key " + keyName + " has incorrect value - " + e.getMessage(), false); - } - loadedLfs.add(keyName.substring(3)); - } else if (keyName.equals("identityref1")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("simple-data-types:iden", jReader.nextString()); - lfidentityrefChecked = true; - } else if (keyName.equals("lfstr")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("lfstr", jReader.nextString()); - lfstrChecked = true; - } else if (keyName.equals("lfstr1")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("", jReader.nextString()); - lfstr1Checked = true; - } else if (keyName.equals("lfbool1")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek); - assertEquals(true, jReader.nextBoolean()); - lfbool1Checked = true; - } else if (keyName.equals("lfbool2")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek); - assertEquals(false, jReader.nextBoolean()); - lfbool2Checked = true; - } else if (keyName.equals("lfbool3")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek); - assertEquals(false, jReader.nextBoolean()); - } else if (keyName.equals("lfdecimal1")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - assertEquals(new Double(43.32), (Double) jReader.nextDouble()); - lfdecimal1Checked = true; - } else if (keyName.equals("lfdecimal2")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - assertEquals(new Double(-0.43), (Double) jReader.nextDouble()); - lfdecimal2Checked = true; - } else if (keyName.equals("lfdecimal3")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - assertEquals(new Double(43), (Double) jReader.nextDouble()); - lfdecimal3Checked = true; - } else if (keyName.equals("lfdecimal4")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - assertEquals(new Double(43E3), (Double) jReader.nextDouble()); - lfdecimal4Checked = true; - } else if (keyName.equals("lfdecimal6")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); - assertEquals(new Double(33.12345), (Double) jReader.nextDouble()); - lfdecimal6Checked = true; - } else if (keyName.equals("lfenum")) { - assertEquals("enum3", jReader.nextString()); - enumChecked = true; - } else if (keyName.equals("lfbits")) { - assertEquals("bit3 bit2", jReader.nextString()); - bitsChecked = true; - } else if (keyName.equals("lfbinary")) { - assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", jReader.nextString()); - lfbinaryChecked = true; - } else if (keyName.equals("lfempty")) { - jReader.beginArray(); - jReader.nextNull(); - jReader.endArray(); - lfemptyChecked = true; - } else if (keyName.startsWith("lfunion")) { - checkLfUnion(jReader, keyName, peek); - } else { - assertTrue("Key " + keyName + " doesn't exists in yang file.", false); + LeafVerifier verifier = expectedMap.remove( keyName ); + assertNotNull( "Found unexpected leaf: " + keyName , verifier ); + + JsonToken expToken = verifier.expectedTokenType(); + if( expToken != null ) { + assertEquals( "Json token type for key " + keyName, expToken, peek ); } + verifier.verify( jReader, keyName );; + } + + if( !expectedMap.isEmpty() ) { + fail( "Missing leaf nodes in Json output: " +expectedMap.keySet() ); } - Collections.sort(loadedLfs); - String expectedLfsStr = "[int16Max, int16Min, int32Max, int32Min, int64Max, int64Min, int8Max, int8Min, uint16Max, uint32Max, uint8Max]"; - String actualLfsStr = loadedLfs.toString(); - assertEquals("Some leaves are missing", expectedLfsStr, actualLfsStr); - assertTrue("Enum wasn't checked", enumChecked); - assertTrue("Bits wasn't checked", bitsChecked); - assertTrue("Decimal1 wasn't checked", lfdecimal1Checked); - assertTrue("Decimal2 wasn't checked", lfdecimal2Checked); - assertTrue("Decimal3 wasn't checked", lfdecimal3Checked); - assertTrue("Decimal4 wasn't checked", lfdecimal4Checked); - assertTrue("Decimal5 wasn't checked", lfdecimal6Checked); - assertTrue("lfbool1 wasn't checked", lfbool1Checked); - assertTrue("lfbool2 wasn't checked", lfbool2Checked); - assertTrue("lfstr wasn't checked", lfstrChecked); - assertTrue("lfstr1 wasn't checked", lfstr1Checked); - assertTrue("lfbinary wasn't checked", lfbinaryChecked); - assertTrue("lfempty wasn't checked", lfemptyChecked); - assertTrue("lfidentityref wasn't checked", lfidentityrefChecked); + jReader.endObject(); } - private void checkLfUnion(JsonReader jReader, String keyName, JsonToken peek) throws IOException { - if (keyName.equals("lfunion1")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("324", jReader.nextString()); - } else if (keyName.equals("lfunion2")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("33.3", jReader.nextString()); - } else if (keyName.equals("lfunion3")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("55", jReader.nextString()); - } else if (keyName.equals("lfunion4")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("true", jReader.nextString()); - } else if (keyName.equals("lfunion5")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("true", jReader.nextString()); - } else if (keyName.equals("lfunion6")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("false", jReader.nextString()); - } else if (keyName.equals("lfunion7")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("", jReader.nextString()); - } else if (keyName.equals("lfunion8")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("", jReader.nextString()); - } else if (keyName.equals("lfunion9")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("", jReader.nextString()); - } else if (keyName.equals("lfunion10")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("bt1", jReader.nextString()); - } else if (keyName.equals("lfunion11")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("33", jReader.nextString()); - } else if (keyName.equals("lfunion12")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("false", jReader.nextString()); - } else if (keyName.equals("lfunion13")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("44", jReader.nextString()); - } else if (keyName.equals("lfunion14")) { - assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); - assertEquals("21", jReader.nextString()); + @Test + public void testBadData() throws Exception { + + try { + CompositeNode compositeNode = TestUtils.readInputToCnSn( + "/cnsn-to-json/simple-data-types/xml/bad-data.xml", + XmlToCompositeNodeProvider.INSTANCE); + + TestUtils.normalizeCompositeNode(compositeNode, modules, "simple-data-types:cont"); + fail( "Expected RestconfDocumentedException" ); + } + catch( RestconfDocumentedException e ) { + assertEquals( "getErrorTag", ErrorTag.INVALID_VALUE, e.getErrors().get( 0 ).getErrorTag() ); } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java index fc54795fcc..155ee9d590 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java @@ -11,12 +11,16 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.util.List; import javax.ws.rs.WebApplicationException; import javax.xml.transform.TransformerFactoryConfigurationError; import org.junit.BeforeClass; import org.junit.Test; + +import static org.mockito.Mockito.*; + import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider; import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; @@ -26,6 +30,29 @@ import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair; +import org.opendaylight.yangtools.yang.model.util.BinaryType; +import org.opendaylight.yangtools.yang.model.util.BitsType; +import org.opendaylight.yangtools.yang.model.util.BooleanType; +import org.opendaylight.yangtools.yang.model.util.EmptyType; +import org.opendaylight.yangtools.yang.model.util.EnumerationType; +import org.opendaylight.yangtools.yang.model.util.Int16; +import org.opendaylight.yangtools.yang.model.util.Int32; +import org.opendaylight.yangtools.yang.model.util.Int64; +import org.opendaylight.yangtools.yang.model.util.Int8; +import org.opendaylight.yangtools.yang.model.util.StringType; +import org.opendaylight.yangtools.yang.model.util.Uint16; +import org.opendaylight.yangtools.yang.model.util.Uint32; +import org.opendaylight.yangtools.yang.model.util.Uint64; +import org.opendaylight.yangtools.yang.model.util.Uint8; +import org.opendaylight.yangtools.yang.model.util.UnionType; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; /** * @@ -65,7 +92,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangStringToXmlTest() { serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.STRING_DEFAULT_CODEC.deserialize("lfStr value"), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(StringType.getInstance()).deserialize("lfStr value"), "lfStr"), "lfStr value"); } @@ -73,7 +100,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangInt8ToXmlTest() { String elName = "lfInt8"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.INT8_DEFAULT_CODEC.deserialize("14"), elName), "<" + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int8.getInstance()).deserialize("14"), elName), "<" + elName + ">14"); } @@ -81,7 +108,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangInt16ToXmlTest() { String elName = "lfInt16"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.INT16_DEFAULT_CODEC.deserialize("3000"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int16.getInstance()).deserialize("3000"), elName), "<" + elName + ">3000"); } @@ -89,7 +116,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangInt32ToXmlTest() { String elName = "lfInt32"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.INT32_DEFAULT_CODEC.deserialize("201234"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int32.getInstance()).deserialize("201234"), elName), "<" + elName + ">201234"); } @@ -97,7 +124,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangInt64ToXmlTest() { String elName = "lfInt64"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.INT64_DEFAULT_CODEC.deserialize("5123456789"), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int64.getInstance()).deserialize("5123456789"), elName), "<" + elName + ">5123456789"); } @@ -105,7 +132,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangUint8ToXmlTest() { String elName = "lfUint8"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT8_DEFAULT_CODEC.deserialize("200"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint8.getInstance()).deserialize("200"), elName), "<" + elName + ">200"); } @@ -113,7 +140,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangUint16ToXmlTest() { String elName = "lfUint16"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT16_DEFAULT_CODEC.deserialize("4000"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint16.getInstance()).deserialize("4000"), elName), "<" + elName + ">4000"); } @@ -121,7 +148,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangUint32ToXmlTest() { String elName = "lfUint32"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT32_DEFAULT_CODEC.deserialize("4123456789"), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint32.getInstance()).deserialize("4123456789"), elName), "<" + elName + ">4123456789"); } @@ -129,7 +156,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangUint64ToXmlTest() { String elName = "lfUint64"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT64_DEFAULT_CODEC.deserialize("5123456789"), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint64.getInstance()).deserialize("5123456789"), elName), "<" + elName + ">5123456789"); } @@ -138,25 +165,40 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { String elName = "lfBinary"; serializeToXml( prepareCnStructForYangData( - TypeDefinitionAwareCodec.BINARY_DEFAULT_CODEC - .deserialize("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567"), + TypeDefinitionAwareCodec.from(BinaryType.getInstance()) + .deserialize("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567"), elName), "<" + elName + ">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567"); + + elName + ">"); } @Test public void snAsYangBitsToXmlTest() { + BitsTypeDefinition.Bit mockBit1 = mock( BitsTypeDefinition.Bit.class ); + when( mockBit1.getName() ).thenReturn( "one" ); + BitsTypeDefinition.Bit mockBit2 = mock( BitsTypeDefinition.Bit.class ); + when( mockBit2.getName() ).thenReturn( "two" ); + List bitList = Lists.newArrayList( mockBit1, mockBit2 ); + String elName = "lfBits"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.deserialize("one two"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from( + BitsType.create( mock( SchemaPath.class ), bitList ) ) + .deserialize("one two"), elName), "<" + elName + ">one two", "<" + elName + ">two one"); } @Test public void snAsYangEnumerationToXmlTest() { + EnumTypeDefinition.EnumPair mockEnum = mock( EnumTypeDefinition.EnumPair.class ); + when( mockEnum.getName() ).thenReturn( "enum2" ); + List enumList = Lists.newArrayList( mockEnum ); + String elName = "lfEnumeration"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.ENUMERATION_DEFAULT_CODEC.deserialize("enum2"), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from( + EnumerationType.create( mock( SchemaPath.class ), enumList, + Optional.absent() ) ) + .deserialize("enum2"), elName), "<" + elName + ">enum2"); } @@ -164,7 +206,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangEmptyToXmlTest() { String elName = "lfEmpty"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.EMPTY_DEFAULT_CODEC.deserialize(null), elName), "<" + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(EmptyType.getInstance()).deserialize(null), elName), "<" + elName + "/>"); } @@ -172,33 +214,46 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { public void snAsYangBooleanToXmlTest() { String elName = "lfBoolean"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.BOOLEAN_DEFAULT_CODEC.deserialize("str"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("str"), elName), "<" + elName + ">false"); serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.BOOLEAN_DEFAULT_CODEC.deserialize("true"), elName), + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("true"), elName), "<" + elName + ">true"); } @Test public void snAsYangUnionToXmlTest() { + + BitsTypeDefinition.Bit mockBit1 = mock( BitsTypeDefinition.Bit.class ); + when( mockBit1.getName() ).thenReturn( "first" ); + BitsTypeDefinition.Bit mockBit2 = mock( BitsTypeDefinition.Bit.class ); + when( mockBit1.getName() ).thenReturn( "second" ); + List bitList = Lists.newArrayList( mockBit1, mockBit2 ); + + List> types = Lists.>newArrayList( + Int8.getInstance(), + BitsType.create( mock( SchemaPath.class ) , bitList ), + BooleanType.getInstance() ); + UnionType unionType = UnionType.create( types ); + String elName = "lfUnion"; String int8 = "15"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(int8), elName), "<" + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(int8), elName), "<" + elName + ">15"); String bits = "first second"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(bits), elName), "<" + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bits), elName), "<" + elName + ">first second"); String bool = "str"; serializeToXml( - prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(bool), elName), "<" + prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bool), elName), "<" + elName + ">str"); } - private void serializeToXml(final CompositeNode compositeNode, final String... xmlRepresentation) + private void serializeToXml(CompositeNode compositeNode, String... xmlRepresentation) throws TransformerFactoryConfigurationError { String xmlString = ""; try { @@ -220,7 +275,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { } - private CompositeNode prepareIdentityrefData(final String prefix, final boolean valueAsQName) { + private CompositeNode prepareIdentityrefData(String prefix, boolean valueAsQName) { MutableCompositeNode cont = NodeFactory.createMutableCompositeNode( TestUtils.buildQName("cont", "basic:module", "2013-12-2"), null, null, ModifyAction.CREATE, null); MutableCompositeNode cont1 = NodeFactory.createMutableCompositeNode( diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java index 3c70cca0f8..47e329cc3e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java @@ -9,8 +9,11 @@ package org.opendaylight.controller.sal.restconf.impl.json.to.cnsn.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -176,6 +179,14 @@ public class JsonToCnSnTest { } + @Test + public void testJsonBlankInput() throws Exception{ + InputStream inputStream = new ByteArrayInputStream( "".getBytes() ); + CompositeNode compositeNode = + JsonToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream); + assertNull( compositeNode ); + } + /** * Tests whether namespace stay unchanged if concrete values are * present in composite or simple node and if the method for update is diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java index 51687e2a12..307abebdd7 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java @@ -56,6 +56,6 @@ public class CodecsExceptionsCatchingTest extends JerseyTest { Response response = target("/config/number:cont").request(MediaType.APPLICATION_XML).put( Entity.entity("3f", MediaType.APPLICATION_XML)); String exceptionMessage = response.readEntity(String.class); - assertTrue(exceptionMessage.contains("Incorrect lexical representation of Integer value: 3f")); + assertTrue(exceptionMessage.contains("invalid-value")); } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java index c0c86c3f25..910ca8e20a 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -308,7 +308,7 @@ public class InvokeRpcMethodTest { ListenableFuture> mockListener = mock( ListenableFuture.class ); when( mockListener.get() ).thenReturn( rpcResult ); - QName cancelToastQName = QName.create( "cancelToast" ); + QName cancelToastQName = QName.create( "namespace", "2014-05-28", "cancelToast" ); RpcDefinition mockRpc = mock( RpcDefinition.class ); when( mockRpc.getQName() ).thenReturn( cancelToastQName ); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java index ce460fe474..cfbc9fdb76 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java @@ -149,6 +149,8 @@ public class RestPostOperationTest extends JerseyTest { mockCommitConfigurationDataPostMethod(TransactionStatus.FAILED); assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath)); + + assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" )); } @Test @@ -172,6 +174,8 @@ public class RestPostOperationTest extends JerseyTest { assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData4)); uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont"; assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData3)); + + assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" )); } private void mockInvokeRpc(CompositeNode result, boolean sucessful) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java index 3af2945526..77b39b7352 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java @@ -22,6 +22,7 @@ import java.util.concurrent.Future; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; @@ -31,6 +32,7 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.core.api.mount.MountService; import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider; +import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper; import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider; import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider; import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider; @@ -86,6 +88,7 @@ public class RestPutOperationTest extends JerseyTest { resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE, StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE, JsonToCompositeNodeProvider.INSTANCE); + resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class ); return resourceConfig; } @@ -100,6 +103,15 @@ public class RestPutOperationTest extends JerseyTest { mockCommitConfigurationDataPutMethod(TransactionStatus.FAILED); assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + + assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" )); + } + + @Test + public void putConfigStatusCodesEmptyBody() throws UnsupportedEncodingException { + String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + Response resp = target(uri).request( MediaType.APPLICATION_JSON).put(Entity.entity( "", MediaType.APPLICATION_JSON)); + assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" )); } @Test diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java index 5008d28bbf..5cda4a7f52 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java @@ -9,8 +9,12 @@ package org.opendaylight.controller.sal.restconf.impl.xml.to.cnsn.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider; @@ -52,4 +56,27 @@ public class XmlToCnSnTest extends YangAndXmlAndDataSchemaLoader { assertEquals("121", lf2.getValue()); } + @Test + public void testXmlBlankInput() throws Exception{ + InputStream inputStream = new ByteArrayInputStream( "".getBytes() ); + CompositeNode compositeNode = + XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream); + + assertNull( compositeNode ); + } + + @Test + public void testXmlBlankInputUnmarkableStream() throws Exception{ + InputStream inputStream = new ByteArrayInputStream( "".getBytes() ){ + @Override + public boolean markSupported() { + return false; + } + }; + CompositeNode compositeNode = + XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream); + + assertNull( compositeNode ); + } + } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml new file mode 100644 index 0000000000..110c3237de --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml @@ -0,0 +1,3 @@ + + invalid + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml index 56872a337d..f73ce1b65c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml @@ -29,15 +29,14 @@ 55 true true - false + 10 bt1 33 false - 44 - 21 - + b1 + zero x:iden \ No newline at end of file diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/NodeStatisticsHandler.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/NodeStatisticsHandler.java index dbcbab982a..7d9cc7ecbd 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/NodeStatisticsHandler.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/NodeStatisticsHandler.java @@ -313,6 +313,9 @@ public final class NodeStatisticsHandler implements AutoCloseable, FlowCapableCo meterStats.close(); queueStats.close(); + //Clean up queued statistics request from scheduler queue + srScheduler.removeRequestsFromSchedulerQueue(this.getNodeRef()); + logger.debug("Statistics handler for {} shut down", targetNodeKey.getId()); } diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsRequestScheduler.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsRequestScheduler.java index 9ebfd6fd62..bea43ef68a 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsRequestScheduler.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsRequestScheduler.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction.DataTransactionListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +63,19 @@ public class StatisticsRequestScheduler implements DataTransactionListener { requestQueue.put(statsRequest, null); } + public void removeRequestsFromSchedulerQueue(NodeRef node){ + AbstractStatsTracker stats = null; + synchronized(requestQueue){ + Iterator> nodesItr = requestQueue.entrySet().iterator(); + while(nodesItr.hasNext()){ + stats = nodesItr.next().getKey(); + if(stats.getNodeRef().equals(node)){ + nodesItr.remove(); + } + } + } + + } public AbstractStatsTracker getNextRequestFromSchedulerQueue(){ //Remove first element AbstractStatsTracker stats = null; @@ -79,10 +93,7 @@ public class StatisticsRequestScheduler implements DataTransactionListener { private void requestStatistics(){ AbstractStatsTracker stats = this.getNextRequestFromSchedulerQueue(); - if(stats != null) { - stats.request(); - stats.increaseRequestCounter(); - } + sendStatsRequest(stats); } @Override public void onStatusUpdated(DataModificationTransaction transaction, TransactionStatus status) { @@ -106,12 +117,19 @@ public class StatisticsRequestScheduler implements DataTransactionListener { break; } } + sendStatsRequest(stats); + } + + private void sendStatsRequest(AbstractStatsTracker stats){ if(stats != null){ - stats.request(); - stats.increaseRequestCounter(); + try{ + stats.request(); + stats.increaseRequestCounter(); + }catch(Exception e){ + srsLogger.warn("Statistics request was not sent successfully. Reason : {}",e.getMessage()); + } } } - public void start(){ timer.schedule(task, 0, REQUEST_MONITOR_INTERVAL); } diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java index 78a2043e20..22a3f10547 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java @@ -8,7 +8,17 @@ package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + import com.google.common.collect.Sets; +import java.util.Map; +import javax.management.Attribute; +import javax.management.ObjectName; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -16,17 +26,6 @@ import org.mockito.MockitoAnnotations; import org.opendaylight.controller.config.util.ConfigTransactionClient; import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; -import javax.management.Attribute; -import javax.management.ObjectName; -import java.util.Map; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - public class ReplaceEditConfigStrategyTest { @Mock @@ -35,7 +34,7 @@ public class ReplaceEditConfigStrategyTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - doNothing().when(ta).destroyConfigBean(anyString(), anyString()); + doNothing().when(ta).destroyModule(anyString(), anyString()); doReturn(mockON()).when(ta).lookupConfigBean(anyString(), anyString()); doNothing().when(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class)); } diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java index 799674487f..829ac304bd 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java @@ -7,7 +7,7 @@ */ package org.opendaylight.controller.netconf.client; -import io.netty.channel.socket.SocketChannel; +import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; import java.io.IOException; import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer; @@ -31,7 +31,7 @@ final class SshClientChannelInitializer extends AbstractChannelInitializer promise) { + public void initialize(final Channel ch, final Promise promise) { try { final Invoker invoker = Invoker.subsystem("netconf"); ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker)); @@ -42,7 +42,7 @@ final class SshClientChannelInitializer extends AbstractChannelInitializer promise) { ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR, negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() { diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializer.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializer.java index 4a0a089fae..ee8f8baf01 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializer.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializer.java @@ -7,7 +7,7 @@ */ package org.opendaylight.controller.netconf.client; -import io.netty.channel.socket.SocketChannel; +import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer; import org.opendaylight.protocol.framework.SessionListenerFactory; @@ -24,12 +24,7 @@ class TcpClientChannelInitializer extends AbstractChannelInitializer promise) { - super.initialize(ch, promise); - } - - @Override - protected void initializeSessionNegotiator(final SocketChannel ch, final Promise promise) { + protected void initializeSessionNegotiator(final Channel ch, final Promise promise) { ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR, negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() { @Override diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java index 60d8f3044a..afa17532d5 100644 --- a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java @@ -8,24 +8,35 @@ package org.opendaylight.controller.netconf.client.test; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.HashedWheelTimer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GlobalEventExecutor; import java.io.Closeable; import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; - import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.NetconfClientSession; import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; - -import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; -import io.netty.util.concurrent.Future; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.protocol.framework.NeverReconnectStrategy; /** @@ -95,4 +106,29 @@ public class TestingNetconfClient implements Closeable { Preconditions.checkState(clientSession != null, "Client was not initialized successfully"); return Sets.newHashSet(clientSession.getServerCapabilities()); } + + public static void main(String[] args) throws Exception { + HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(); + NioEventLoopGroup nettyGroup = new NioEventLoopGroup(); + NetconfClientDispatcherImpl netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer); + LoginPassword authHandler = new LoginPassword("admin", "admin"); + TestingNetconfClient client = new TestingNetconfClient("client", netconfClientDispatcher, getClientConfig("127.0.0.1", 1830, true, Optional.of(authHandler))); + System.out.println(client.getCapabilities()); + } + + private static NetconfClientConfiguration getClientConfig(String host ,int port, boolean ssh, Optional maybeAuthHandler) throws UnknownHostException { + InetSocketAddress netconfAddress = new InetSocketAddress(InetAddress.getByName(host), port); + final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create(); + b.withAddress(netconfAddress); + b.withSessionListener(new SimpleNetconfClientSessionListener()); + b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, + NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS)); + if (ssh) { + b.withProtocol(NetconfClientProtocol.SSH); + b.withAuthHandler(maybeAuthHandler.get()); + } else { + b.withProtocol(NetconfClientProtocol.TCP); + } + return b.build(); + } } diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml index 1d94517152..c60506ef44 100644 --- a/opendaylight/netconf/netconf-impl/pom.xml +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -109,6 +109,7 @@ org.apache.felix maven-bundle-plugin + 2.3.7 org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator @@ -121,6 +122,7 @@ io.netty.buffer, io.netty.handler.codec, io.netty.channel.nio, + io.netty.channel.local, javax.annotation, javax.management, javax.net.ssl, diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java index de3dee1443..4dfb749818 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java @@ -8,8 +8,13 @@ package org.opendaylight.controller.netconf.impl; +import com.google.common.annotations.VisibleForTesting; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalServerChannel; import io.netty.channel.socket.SocketChannel; import io.netty.util.concurrent.Promise; import java.net.InetSocketAddress; @@ -27,6 +32,7 @@ public class NetconfServerDispatcher extends AbstractDispatcher() { @@ -37,6 +43,15 @@ public class NetconfServerDispatcher extends AbstractDispatcher() { + @Override + public void initializeChannel(final LocalChannel ch, final Promise promise) { + initializer.initialize(ch, promise); + } + }); + } + public static class ServerChannelInitializer extends AbstractChannelInitializer { public static final String DESERIALIZER_EX_HANDLER_KEY = "deserializerExHandler"; @@ -50,16 +65,15 @@ public class NetconfServerDispatcher extends AbstractDispatcher promise) { + protected void initializeSessionNegotiator(Channel ch, Promise promise) { ch.pipeline().addAfter(DESERIALIZER_EX_HANDLER_KEY, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR, negotiatorFactory.getSessionNegotiator(null, ch, promise)); } } - } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java index 7130dc3501..6ab62ef29a 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -7,12 +7,13 @@ */ package org.opendaylight.controller.netconf.impl.osgi; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.HashedWheelTimer; import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; import java.util.Dictionary; import java.util.Hashtable; import java.util.concurrent.TimeUnit; - import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; @@ -26,9 +27,6 @@ import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.HashedWheelTimer; - public class NetconfImplActivator implements BundleActivator { private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class); @@ -40,17 +38,16 @@ public class NetconfImplActivator implements BundleActivator { private ServiceRegistration regMonitoring; @Override - public void start(final BundleContext context) { - final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfServerAddress(context, - NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS); - final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + public void start(final BundleContext context) { + + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); startOperationServiceFactoryTracker(context, factoriesListener); - final SessionIdProvider idProvider = new SessionIdProvider(); + SessionIdProvider idProvider = new SessionIdProvider(); timer = new HashedWheelTimer(); - long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context); + commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); SessionMonitoringService monitoringService = startMonitoringService(context, factoriesListener); @@ -62,24 +59,24 @@ public class NetconfImplActivator implements BundleActivator { NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( serverNegotiatorFactory); + NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup); - NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, - eventLoopGroup); - - logger.info("Starting TCP netconf server at {}", address); - dispatch.createServer(address); + LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress(); + logger.trace("Starting local netconf server at {}", address); + dispatch.createLocalServer(address); context.registerService(NetconfOperationProvider.class, factoriesListener, null); + } - private void startOperationServiceFactoryTracker(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) { + private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) { factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener); factoriesTracker.open(); } - private NetconfMonitoringServiceImpl startMonitoringService(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) { - final NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener); - final Dictionary dic = new Hashtable<>(); + private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) { + NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener); + Dictionary dic = new Hashtable<>(); regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic); return netconfMonitoringServiceImpl; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index 140284e4ee..0969bd92a5 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -16,13 +16,13 @@ import static org.mockito.Mockito.mock; import ch.ethz.ssh2.Connection; import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.concurrent.GlobalEventExecutor; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; -import java.nio.file.Files; import java.util.Collection; import java.util.List; import junit.framework.Assert; @@ -50,16 +50,14 @@ import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.opendaylight.controller.sal.authorization.AuthResultEnum; -import org.opendaylight.controller.usermanager.IUserManager; import org.opendaylight.protocol.framework.NeverReconnectStrategy; public class NetconfITSecureTest extends AbstractNetconfConfigTest { private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024); - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); private DefaultCommitNotificationProducer commitNot; private NetconfSSHServer sshServer; @@ -79,13 +77,10 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { final NetconfServerDispatcher dispatchS = createDispatcher(factoriesListener); - ChannelFuture s = dispatchS.createServer(tcpAddress); + ChannelFuture s = dispatchS.createLocalServer(NetconfConfigUtil.getNetconfLocalAddress()); s.await(); - - sshServer = NetconfSSHServer.start(tlsAddress.getPort(), tcpAddress, getAuthProvider()); - Thread thread = new Thread(sshServer); - thread.setDaemon(true); - thread.start(); + EventLoopGroup bossGroup = new NioEventLoopGroup(); + sshServer = NetconfSSHServer.start(tlsAddress.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), getAuthProvider(), bossGroup); } private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) { @@ -140,13 +135,10 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { } public AuthProvider getAuthProvider() throws Exception { - final IUserManager userManager = mock(IUserManager.class); - doReturn(AuthResultEnum.AUTH_ACCEPT).when(userManager).authenticate(anyString(), anyString()); - - final File privateKeyFile = Files.createTempFile("tmp-netconf-test", "pk").toFile(); - privateKeyFile.deleteOnExit(); - String privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile); - return new AuthProvider(userManager, privateKeyPEMString); + AuthProvider mock = mock(AuthProvider.class); + doReturn(true).when(mock).authenticated(anyString(), anyString()); + doReturn(PEMGenerator.generate().toCharArray()).when(mock).getPEMAsCharArray(); + return mock; } public AuthenticationHandler getAuthHandler() throws IOException { diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index fd43f67c05..60a5207daa 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.netconf.it; -import static java.util.Collections.emptyList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -17,6 +16,10 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.netty.channel.ChannelFuture; import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; @@ -29,10 +32,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; - import javax.management.ObjectName; import javax.xml.parsers.ParserConfigurationException; - import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -66,32 +67,20 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity2; import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry; import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import io.netty.channel.ChannelFuture; - public class NetconfITTest extends AbstractNetconfConfigTest { // TODO refactor, pull common code up to AbstractNetconfITTest - private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class); - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); - private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830); - private static final String USERNAME = "netconf"; - private static final String PASSWORD = "netconf"; - private NetconfMessage getConfig, getConfigCandidate, editConfig, - closeSession, startExi, stopExi; + + private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession; private DefaultCommitNotificationProducer commitNot; private NetconfServerDispatcher dispatch; @@ -139,10 +128,6 @@ public class NetconfITTest extends AbstractNetconfConfigTest { this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml"); this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml"); - this.startExi = XmlFileLoader - .xmlFileToNetconfMessage("netconfMessages/startExi.xml"); - this.stopExi = XmlFileLoader - .xmlFileToNetconfMessage("netconfMessages/stopExi.xml"); this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml"); } @@ -166,7 +151,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { yangDependencies.add(resourceAsStream); } } - assertEquals("Some yang files were not found", emptyList(), failedToFind); + assertEquals("Some yang files were not found", Collections.emptyList(), failedToFind); return yangDependencies; } @@ -198,6 +183,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { public void testTwoSessions() throws Exception { try (TestingNetconfClient netconfClient = new TestingNetconfClient("1", clientDispatcher, getClientConfiguration(tcpAddress, 10000))) { try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("2", clientDispatcher, getClientConfiguration(tcpAddress, 10000))) { + assertNotNull(netconfClient2.getCapabilities()); } } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractChannelInitializer.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractChannelInitializer.java index e88bf53ae0..7897666ddc 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractChannelInitializer.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractChannelInitializer.java @@ -8,7 +8,7 @@ package org.opendaylight.controller.netconf.nettyutil; -import io.netty.channel.socket.SocketChannel; +import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.nettyutil.handler.FramingMechanismHandlerFactory; @@ -25,7 +25,7 @@ public abstract class AbstractChannelInitializer { public static final String NETCONF_MESSAGE_FRAME_ENCODER = "frameEncoder"; public static final String NETCONF_SESSION_NEGOTIATOR = "negotiator"; - public void initialize(SocketChannel ch, Promise promise) { + public void initialize(Channel ch, Promise promise) { ch.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfEOMAggregator()); initializeMessageDecoder(ch); ch.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER, FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM)); @@ -34,13 +34,13 @@ public abstract class AbstractChannelInitializer { initializeSessionNegotiator(ch, promise); } - protected void initializeMessageEncoder(SocketChannel ch) { + protected void initializeMessageEncoder(Channel ch) { // Special encoding handler for hello message to include additional header if available, // it is thrown away after successful negotiation ch.pipeline().addLast(NETCONF_MESSAGE_ENCODER, new NetconfHelloMessageToXMLEncoder()); } - protected void initializeMessageDecoder(SocketChannel ch) { + protected void initializeMessageDecoder(Channel ch) { // Special decoding handler for hello message to parse additional header if available, // it is thrown away after successful negotiation ch.pipeline().addLast(NETCONF_MESSAGE_DECODER, new NetconfXMLToHelloMessageDecoder()); @@ -50,6 +50,6 @@ public abstract class AbstractChannelInitializer { * Insert session negotiator into the pipeline. It must be inserted after message decoder * identified by {@link AbstractChannelInitializer#NETCONF_MESSAGE_DECODER}, (or any other custom decoder processor) */ - protected abstract void initializeSessionNegotiator(SocketChannel ch, Promise promise); + protected abstract void initializeSessionNegotiator(Channel ch, Promise promise); } diff --git a/opendaylight/netconf/netconf-ssh/pom.xml b/opendaylight/netconf/netconf-ssh/pom.xml index 622881352e..cbd3efc57f 100644 --- a/opendaylight/netconf/netconf-ssh/pom.xml +++ b/opendaylight/netconf/netconf-ssh/pom.xml @@ -56,21 +56,25 @@ org.apache.felix maven-bundle-plugin + 2.3.7 org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator com.google.common.base, - ch.ethz.ssh2, - ch.ethz.ssh2.signature, - org.apache.commons.io, - org.opendaylight.controller.netconf.util.osgi, - org.opendaylight.controller.usermanager, - org.opendaylight.controller.sal.authorization, - org.opendaylight.controller.sal.utils, - org.osgi.framework, - org.osgi.util.tracker, - org.slf4j, - org.bouncycastle.openssl + ch.ethz.ssh2, + ch.ethz.ssh2.signature, + org.apache.commons.io, + org.opendaylight.controller.netconf.util.osgi, + org.opendaylight.controller.usermanager, + org.opendaylight.controller.sal.authorization, + org.opendaylight.controller.sal.utils, + org.osgi.framework, + org.osgi.util.tracker, + org.slf4j, + org.bouncycastle.openssl, + io.netty.bootstrap, io.netty.buffer, io.netty.channel, io.netty.channel.local, io.netty.channel.nio, + io.netty.handler.stream, io.netty.util.concurrent, org.apache.commons.lang3, + org.opendaylight.controller.netconf.util.messages diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java index c6974d4982..08bf9836b2 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java @@ -7,79 +7,94 @@ */ package org.opendaylight.controller.netconf.ssh; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; -import org.opendaylight.controller.netconf.ssh.threads.SocketThread; -import org.opendaylight.controller.usermanager.IUserManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.concurrent.ThreadSafe; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; import java.io.IOException; -import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; +import org.opendaylight.controller.netconf.ssh.threads.Handshaker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker}, + * which is executed in {@link #handshakeExecutor}. + */ @ThreadSafe -public final class NetconfSSHServer implements Runnable { +public final class NetconfSSHServer extends Thread implements AutoCloseable { - private ServerSocket ss = null; - private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class); - private static final AtomicLong sesssionId = new AtomicLong(); - private final InetSocketAddress clientAddress; - private final AuthProvider authProvider; - private volatile boolean up = false; + private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class); + private static final AtomicLong sessionIdCounter = new AtomicLong(); - private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress, AuthProvider authProvider) throws IllegalStateException, IOException { + private final ServerSocket serverSocket; + private final LocalAddress localAddress; + private final EventLoopGroup bossGroup; + private final AuthProvider authProvider; + private final ExecutorService handshakeExecutor; + private volatile boolean up; - logger.trace("Creating SSH server socket on port {}",serverPort); - this.ss = new ServerSocket(serverPort); - if (!ss.isBound()){ - throw new IllegalStateException("Socket can't be bound to requested port :"+serverPort); + private NetconfSSHServer(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException { + super(NetconfSSHServer.class.getSimpleName()); + this.bossGroup = bossGroup; + logger.trace("Creating SSH server socket on port {}", serverPort); + this.serverSocket = new ServerSocket(serverPort); + if (serverSocket.isBound() == false) { + throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort); } logger.trace("Server socket created."); - this.clientAddress = clientAddress; + this.localAddress = localAddress; this.authProvider = authProvider; this.up = true; + handshakeExecutor = Executors.newFixedThreadPool(10); } - public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress,AuthProvider authProvider) throws IllegalStateException, IOException { - return new NetconfSSHServer(serverPort, clientAddress,authProvider); + public static NetconfSSHServer start(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException { + NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, authProvider, bossGroup); + netconfSSHServer.start(); + return netconfSSHServer; } - public void stop() throws IOException { + @Override + public void close() throws IOException { up = false; logger.trace("Closing SSH server socket."); - ss.close(); + serverSocket.close(); + bossGroup.shutdownGracefully(); logger.trace("SSH server socket closed."); } - public void removeUserManagerService(){ - this.authProvider.removeUserManagerService(); - } - - public void addUserManagerService(IUserManager userManagerService){ - this.authProvider.addUserManagerService(userManagerService); - } - public boolean isUp(){ - return this.up; - } @Override public void run() { while (up) { - logger.trace("Starting new socket thread."); + Socket acceptedSocket = null; try { - SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet(), authProvider); - } - catch (IOException e) { - if( up ) { - logger.error("Exception occurred during socket thread initialization", e); + acceptedSocket = serverSocket.accept(); + } catch (IOException e) { + if (up == false) { + logger.trace("Exiting server thread", e); + } else { + logger.warn("Exception occurred during socket.accept", e); } - else { - // We're shutting down so an exception is expected as the socket's been closed. - // Log to debug. - logger.debug("Shutting down - got expected exception: " + e); + } + if (acceptedSocket != null) { + try { + Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), authProvider, bossGroup); + handshakeExecutor.submit(task); + } catch (IOException e) { + logger.warn("Cannot set PEMHostKey, closing connection", e); + try { + acceptedSocket.close(); + } catch (IOException e1) { + logger.warn("Ignoring exception while closing socket", e); + } } } } + logger.debug("Server thread is exiting"); } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java index 2e9a0b9d8b..5d39dd1eb8 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java @@ -7,41 +7,75 @@ */ package org.opendaylight.controller.netconf.ssh.authentication; -import java.io.IOException; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; import org.opendaylight.controller.sal.authorization.AuthResultEnum; import org.opendaylight.controller.usermanager.IUserManager; -import static com.google.common.base.Preconditions.checkNotNull; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class AuthProvider implements AuthProviderInterface { +public class AuthProvider { + private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class); - private IUserManager um; private final String pem; + private IUserManager nullableUserManager; - public AuthProvider(IUserManager ium, String pemCertificate) throws IllegalArgumentException, IOException { + public AuthProvider(String pemCertificate, final BundleContext bundleContext) { checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null"); - checkNotNull(ium, "No user manager service available."); - this.um = ium; pem = pemCertificate; + + ServiceTrackerCustomizer customizer = new ServiceTrackerCustomizer() { + @Override + public IUserManager addingService(final ServiceReference reference) { + logger.trace("Service {} added", reference); + nullableUserManager = bundleContext.getService(reference); + return nullableUserManager; + } + + @Override + public void modifiedService(final ServiceReference reference, final IUserManager service) { + logger.trace("Replacing modified service {} in netconf SSH.", reference); + nullableUserManager = service; + } + + @Override + public void removedService(final ServiceReference reference, final IUserManager service) { + logger.trace("Removing service {} from netconf SSH. " + + "SSH won't authenticate users until IUserManager service will be started.", reference); + synchronized (AuthProvider.this) { + nullableUserManager = null; + } + } + }; + ServiceTracker listenerTracker = new ServiceTracker<>(bundleContext, IUserManager.class, customizer); + listenerTracker.open(); } - @Override - public boolean authenticated(String username, String password) { - AuthResultEnum authResult = this.um.authenticate(username, password); + /** + * Authenticate user. This implementation tracks IUserManager and delegates the decision to it. If the service is not + * available, IllegalStateException is thrown. + */ + public synchronized boolean authenticated(String username, String password) { + if (nullableUserManager == null) { + logger.warn("Cannot authenticate user '{}', user manager service is missing", username); + throw new IllegalStateException("User manager service is not available"); + } + AuthResultEnum authResult = nullableUserManager.authenticate(username, password); + logger.debug("Authentication result for user '{}' : {}", username, authResult); return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC); } - @Override public char[] getPEMAsCharArray() { return pem.toCharArray(); } - @Override - public void removeUserManagerService() { - this.um = null; - } - - @Override - public void addUserManagerService(IUserManager userManagerService) { - this.um = userManagerService; + @VisibleForTesting + void setNullableUserManager(IUserManager nullableUserManager) { + this.nullableUserManager = nullableUserManager; } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java deleted file mode 100644 index fad0f79a4e..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java +++ /dev/null @@ -1,19 +0,0 @@ - -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.netconf.ssh.authentication; - -import org.opendaylight.controller.usermanager.IUserManager; - -public interface AuthProviderInterface { - - public boolean authenticated(String username, String password) throws IllegalStateException; - public char[] getPEMAsCharArray() throws Exception; - public void removeUserManagerService(); - public void addUserManagerService(IUserManager userManagerService); -} diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java index 348fe006f3..53ab8219ee 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java @@ -8,8 +8,11 @@ package org.opendaylight.controller.netconf.ssh.authentication; +import com.google.common.annotations.VisibleForTesting; +import java.io.FileInputStream; import java.security.NoSuchAlgorithmException; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.bouncycastle.openssl.PEMWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,17 +29,55 @@ public class PEMGenerator { private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class); private static final int KEY_SIZE = 4096; + + public static String readOrGeneratePK(File privateKeyFile) throws IOException { + if (privateKeyFile.exists() == false) { + // generate & save to file + try { + return generateTo(privateKeyFile); + } catch (Exception e) { + logger.error("Exception occurred while generating PEM string to {}", privateKeyFile, e); + throw new IllegalStateException("Error generating RSA key from file " + privateKeyFile); + } + } else { + // read from file + try (FileInputStream fis = new FileInputStream(privateKeyFile)) { + return IOUtils.toString(fis); + } catch (final IOException e) { + logger.error("Error reading RSA key from file {}", privateKeyFile, e); + throw new IOException("Error reading RSA key from file " + privateKeyFile, e); + } + } + } + + /** + * Generate private key to a file and return its content as string. + * + * @param privateFile path where private key should be generated + * @return String representation of private key + * @throws IOException + * @throws NoSuchAlgorithmException + */ + @VisibleForTesting public static String generateTo(File privateFile) throws IOException, NoSuchAlgorithmException { + logger.info("Generating private key to {}", privateFile.getAbsolutePath()); + String privatePEM = generate(); + FileUtils.write(privateFile, privatePEM); + return privatePEM; + } + + @VisibleForTesting + public static String generate() throws NoSuchAlgorithmException, IOException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); SecureRandom sr = new SecureRandom(); keyGen.initialize(KEY_SIZE, sr); KeyPair keypair = keyGen.generateKeyPair(); - logger.info("Generating private key to {}", privateFile.getAbsolutePath()); - String privatePEM = toString(keypair.getPrivate()); - FileUtils.write(privateFile, privatePEM); - return privatePEM; + return toString(keypair.getPrivate()); } + /** + * Get string representation of a key. + */ private static String toString(Key key) throws IOException { try (StringWriter writer = new StringWriter()) { try (PEMWriter pemWriter = new PEMWriter(writer)) { @@ -45,4 +86,5 @@ public class PEMGenerator { return writer.toString(); } } + } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java index d74308cfad..a26843fae1 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java @@ -7,24 +7,24 @@ */ package org.opendaylight.controller.netconf.ssh.osgi; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Optional; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.nio.NioEventLoopGroup; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; -import org.opendaylight.controller.usermanager.IUserManager; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; -import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,112 +32,56 @@ import org.slf4j.LoggerFactory; * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket * and listens for client connections. Each client connection creation is handled in separate - * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread. + * {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker} thread. * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread} * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream. * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish. * All threads are daemons. - **/ -public class NetconfSSHActivator implements BundleActivator{ + */ +public class NetconfSSHActivator implements BundleActivator { + private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class); private NetconfSSHServer server; - private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class); - private IUserManager iUserManager; - private BundleContext context = null; - - private ServiceTrackerCustomizer customizer = new ServiceTrackerCustomizer(){ - @Override - public IUserManager addingService(final ServiceReference reference) { - logger.trace("Service {} added, let there be SSH bridge.", reference); - iUserManager = context.getService(reference); - try { - onUserManagerFound(iUserManager); - } catch (final Exception e) { - logger.trace("Can't start SSH server due to {}",e); - } - return iUserManager; - } - @Override - public void modifiedService(final ServiceReference reference, final IUserManager service) { - logger.trace("Replacing modified service {} in netconf SSH.", reference); - server.addUserManagerService(service); - } - @Override - public void removedService(final ServiceReference reference, final IUserManager service) { - logger.trace("Removing service {} from netconf SSH. " + - "SSH won't authenticate users until IUserManager service will be started.", reference); - removeUserManagerService(); - } - }; - @Override - public void start(final BundleContext context) { - this.context = context; - listenForManagerService(); + public void start(final BundleContext bundleContext) throws IOException { + server = startSSHServer(bundleContext); } @Override public void stop(BundleContext context) throws IOException { - if (server != null){ - server.stop(); - logger.trace("Netconf SSH bridge is down ..."); + if (server != null) { + server.close(); } } - private void startSSHServer() throws IOException { - checkNotNull(this.iUserManager, "No user manager service available."); - logger.trace("Starting netconf SSH bridge."); - final InetSocketAddress sshSocketAddress = NetconfConfigUtil.extractSSHNetconfAddress(context, - NetconfConfigUtil.DEFAULT_NETCONF_SSH_ADDRESS); - final InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfClientAddress(context, - NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS); - String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(context)); + private static NetconfSSHServer startSSHServer(BundleContext bundleContext) throws IOException { + Optional maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, + InfixProp.ssh); - if (path.isEmpty()) { - throw new IllegalStateException("Missing netconf.ssh.pk.path key in configuration file."); + if (maybeSshSocketAddress.isPresent() == false) { + logger.trace("SSH bridge not configured"); + return null; } + InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get(); + logger.trace("Starting netconf SSH bridge at {}", sshSocketAddress); - final File privateKeyFile = new File(path); - final String privateKeyPEMString; - if (privateKeyFile.exists() == false) { - // generate & save to file - try { - privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile); - } catch (Exception e) { - logger.error("Exception occurred while generating PEM string {}", e); - throw new IllegalStateException("Error generating RSA key from file " + path); - } - } else { - // read from file - try (FileInputStream fis = new FileInputStream(path)) { - privateKeyPEMString = IOUtils.toString(fis); - } catch (final IOException e) { - logger.error("Error reading RSA key from file '{}'", path); - throw new IOException("Error reading RSA key from file " + path, e); - } - } - final AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString); - this.server = NetconfSSHServer.start(sshSocketAddress.getPort(), tcpSocketAddress, authProvider); + LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); + + String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext)); + checkState(StringUtils.isNotBlank(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey()); + String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path)); + + final AuthProvider authProvider = new AuthProvider(privateKeyPEMString, bundleContext); + EventLoopGroup bossGroup = new NioEventLoopGroup(); + NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, authProvider, bossGroup); final Thread serverThread = new Thread(server, "netconf SSH server thread"); serverThread.setDaemon(true); serverThread.start(); logger.trace("Netconf SSH bridge up and running."); + return server; } - private void onUserManagerFound(final IUserManager userManager) throws Exception{ - if (server!=null && server.isUp()){ - server.addUserManagerService(userManager); - } else { - startSSHServer(); - } - } - private void removeUserManagerService(){ - this.server.removeUserManagerService(); - } - private void listenForManagerService(){ - final ServiceTracker listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer); - listenerTracker.open(); - } + } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java new file mode 100644 index 0000000000..d999d378d9 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.netconf.ssh.threads; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import ch.ethz.ssh2.AuthenticationResult; +import ch.ethz.ssh2.PtySettings; +import ch.ethz.ssh2.ServerAuthenticationCallback; +import ch.ethz.ssh2.ServerConnection; +import ch.ethz.ssh2.ServerConnectionCallback; +import ch.ethz.ssh2.ServerSession; +import ch.ethz.ssh2.ServerSessionCallback; +import ch.ethz.ssh2.SimpleServerSessionCallback; +import com.google.common.base.Supplier; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufProcessor; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.handler.stream.ChunkedStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import javax.annotation.concurrent.NotThreadSafe; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * One instance represents per connection, responsible for ssh handshake. + * Once auth succeeds and correct subsystem is chosen, backend connection with + * netty netconf server is made. This task finishes right after negotiation is done. + */ +@ThreadSafe +public class Handshaker implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(Handshaker.class); + + private final ServerConnection ganymedConnection; + private final String session; + + + public Handshaker(Socket socket, LocalAddress localAddress, long sessionId, AuthProvider authProvider, + EventLoopGroup bossGroup) throws IOException { + + this.session = "Session " + sessionId; + + String remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replace("/", ""); + logger.debug("{} started with {}", session, remoteAddressWithPort); + String remoteAddress, remotePort; + if (remoteAddressWithPort.contains(":")) { + String[] split = remoteAddressWithPort.split(":"); + remoteAddress = split[0]; + remotePort = split[1]; + } else { + remoteAddress = remoteAddressWithPort; + remotePort = ""; + } + ServerAuthenticationCallbackImpl serverAuthenticationCallback = new ServerAuthenticationCallbackImpl( + authProvider, session); + + ganymedConnection = new ServerConnection(socket); + + ServerConnectionCallbackImpl serverConnectionCallback = new ServerConnectionCallbackImpl( + serverAuthenticationCallback, remoteAddress, remotePort, session, + getGanymedAutoCloseable(ganymedConnection), localAddress, bossGroup); + + // initialize ganymed + ganymedConnection.setPEMHostKey(authProvider.getPEMAsCharArray(), null); + ganymedConnection.setAuthenticationCallback(serverAuthenticationCallback); + ganymedConnection.setServerConnectionCallback(serverConnectionCallback); + } + + + private static AutoCloseable getGanymedAutoCloseable(final ServerConnection ganymedConnection) { + return new AutoCloseable() { + @Override + public void close() throws Exception { + ganymedConnection.close(); + } + }; + } + + @Override + public void run() { + // let ganymed process handshake + logger.trace("{} SocketThread is started", session); + try { + // TODO this should be guarded with a timer to prevent resource exhaustion + ganymedConnection.connect(); + } catch (IOException e) { + logger.warn("{} SocketThread error ", session, e); + } + logger.trace("{} SocketThread is exiting", session); + } +} + +/** + * Netty client handler that forwards bytes from backed server to supplied output stream. + * When backend server closes the connection, remoteConnection.close() is called to tear + * down ssh connection. + */ +class SSHClientHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class); + private final AutoCloseable remoteConnection; + private final OutputStream remoteOutputStream; + private final String session; + private ChannelHandlerContext channelHandlerContext; + + public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream, + String session) { + this.remoteConnection = remoteConnection; + this.remoteOutputStream = remoteOutputStream; + this.session = session; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + this.channelHandlerContext = ctx; + logger.debug("{} Client active", session); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ByteBuf bb = (ByteBuf) msg; + // we can block the server here so that slow client does not cause memory pressure + try { + bb.forEachByte(new ByteBufProcessor() { + @Override + public boolean process(byte value) throws Exception { + remoteOutputStream.write(value); + return true; + } + }); + } finally { + bb.release(); + } + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws IOException { + logger.trace("{} Flushing", session); + remoteOutputStream.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("{} Unexpected exception from downstream", session, cause); + ctx.close(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + logger.trace("{} channelInactive() called, closing remote client ctx", session); + remoteConnection.close();//this should close socket and all threads created for this client + this.channelHandlerContext = null; + } + + public ChannelHandlerContext getChannelHandlerContext() { + return checkNotNull(channelHandlerContext, "Channel is not active"); + } +} + +/** + * Ganymed handler that gets unencrypted input and output streams, connects them to netty. + * Checks that 'netconf' subsystem is chosen by user. + * Launches new ClientInputStreamPoolingThread thread once session is established. + * Writes custom header to netty server, to inform it about IP address and username. + */ +class ServerConnectionCallbackImpl implements ServerConnectionCallback { + private static final Logger logger = LoggerFactory.getLogger(ServerConnectionCallbackImpl.class); + public static final String NETCONF_SUBSYSTEM = "netconf"; + + private final Supplier currentUserSupplier; + private final String remoteAddress; + private final String remotePort; + private final String session; + private final AutoCloseable ganymedConnection; + private final LocalAddress localAddress; + private final EventLoopGroup bossGroup; + + ServerConnectionCallbackImpl(Supplier currentUserSupplier, String remoteAddress, String remotePort, String session, + AutoCloseable ganymedConnection, LocalAddress localAddress, EventLoopGroup bossGroup) { + this.currentUserSupplier = currentUserSupplier; + this.remoteAddress = remoteAddress; + this.remotePort = remotePort; + this.session = session; + this.ganymedConnection = ganymedConnection; + // initialize netty local connection + this.localAddress = localAddress; + this.bossGroup = bossGroup; + } + + private static ChannelFuture initializeNettyConnection(LocalAddress localAddress, EventLoopGroup bossGroup, + final SSHClientHandler sshClientHandler) { + Bootstrap clientBootstrap = new Bootstrap(); + clientBootstrap.group(bossGroup).channel(LocalChannel.class); + + clientBootstrap.handler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(sshClientHandler); + } + }); + // asynchronously initialize local connection to netconf server + return clientBootstrap.connect(localAddress); + } + + @Override + public ServerSessionCallback acceptSession(final ServerSession serverSession) { + String currentUser = currentUserSupplier.get(); + final String additionalHeader = new NetconfHelloMessageAdditionalHeader(currentUser, remoteAddress, + remotePort, "ssh", "client").toFormattedString(); + + + return new SimpleServerSessionCallback() { + @Override + public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException { + return new Runnable() { + @Override + public void run() { + if (NETCONF_SUBSYSTEM.equals(subsystem)) { + // connect + final SSHClientHandler sshClientHandler = new SSHClientHandler(ganymedConnection, ss.getStdin(), session); + ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler); + // get channel + final Channel channel = clientChannelFuture.awaitUninterruptibly().channel(); + new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() { + @Override + public void close() throws Exception { + logger.trace("Closing both ganymed and local connection"); + try { + ganymedConnection.close(); + } catch (Exception e) { + logger.warn("Ignoring exception while closing ganymed", e); + } + try { + channel.close(); + } catch (Exception e) { + logger.warn("Ignoring exception while closing channel", e); + } + } + }, sshClientHandler.getChannelHandlerContext()).start(); + + // write additional header + channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes())); + } else { + logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem); + String reason = "Only netconf subsystem is supported, requested:" + subsystem; + closeSession(ss, reason); + } + } + }; + } + + public void closeSession(ServerSession ss, String reason) { + logger.trace("{} Closing session - {}", serverSession, reason); + try { + ss.getStdin().write(reason.getBytes()); + } catch (IOException e) { + logger.warn("{} Exception while closing session", serverSession, e); + } + ss.close(); + } + + @Override + public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException { + return new Runnable() { + @Override + public void run() { + closeSession(ss, "PTY request not supported"); + } + }; + } + + @Override + public Runnable requestShell(final ServerSession ss) throws IOException { + return new Runnable() { + @Override + public void run() { + closeSession(ss, "Shell not supported"); + } + }; + } + }; + } +} + +/** + * Only thread that is required during ssh session, forwards client's input to netty. + * When user closes connection, onEndOfInput.close() is called to tear down the local channel. + */ +class ClientInputStreamPoolingThread extends Thread { + private static final Logger logger = LoggerFactory.getLogger(ClientInputStreamPoolingThread.class); + + private final InputStream fromClientIS; + private final Channel serverChannel; + private final AutoCloseable onEndOfInput; + private final ChannelHandlerContext channelHandlerContext; + + ClientInputStreamPoolingThread(String session, InputStream fromClientIS, Channel serverChannel, AutoCloseable onEndOfInput, + ChannelHandlerContext channelHandlerContext) { + super(ClientInputStreamPoolingThread.class.getSimpleName() + " " + session); + this.fromClientIS = fromClientIS; + this.serverChannel = serverChannel; + this.onEndOfInput = onEndOfInput; + this.channelHandlerContext = channelHandlerContext; + } + + @Override + public void run() { + ChunkedStream chunkedStream = new ChunkedStream(fromClientIS); + try { + ByteBuf byteBuf; + while ((byteBuf = chunkedStream.readChunk(channelHandlerContext/*only needed for ByteBuf alloc */)) != null) { + serverChannel.writeAndFlush(byteBuf); + } + } catch (Exception e) { + logger.warn("Exception", e); + } finally { + logger.trace("End of input"); + // tear down connection + try { + onEndOfInput.close(); + } catch (Exception e) { + logger.warn("Ignoring exception while closing socket", e); + } + } + } +} + +/** + * Authentication handler for ganymed. + * Provides current user name after authenticating using supplied AuthProvider. + */ +@NotThreadSafe +class ServerAuthenticationCallbackImpl implements ServerAuthenticationCallback, Supplier { + private static final Logger logger = LoggerFactory.getLogger(ServerAuthenticationCallbackImpl.class); + private final AuthProvider authProvider; + private final String session; + private String currentUser; + + ServerAuthenticationCallbackImpl(AuthProvider authProvider, String session) { + this.authProvider = authProvider; + this.session = session; + } + + @Override + public String initAuthentication(ServerConnection sc) { + logger.trace("{} Established connection", session); + return "Established connection" + "\r\n"; + } + + @Override + public String[] getRemainingAuthMethods(ServerConnection sc) { + return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD}; + } + + @Override + public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) { + return AuthenticationResult.FAILURE; + } + + @Override + public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) { + checkState(currentUser == null); + try { + if (authProvider.authenticated(username, password)) { + currentUser = username; + logger.trace("{} user {} authenticated", session, currentUser); + return AuthenticationResult.SUCCESS; + } + } catch (Exception e) { + logger.warn("{} Authentication failed", session, e); + } + return AuthenticationResult.FAILURE; + } + + @Override + public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm, + byte[] publicKey, byte[] signature) { + return AuthenticationResult.FAILURE; + } + + @Override + public String get() { + return currentUser; + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java deleted file mode 100644 index c53a625ad0..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.netconf.ssh.threads; - -import java.io.InputStream; -import java.io.OutputStream; - -import javax.annotation.concurrent.ThreadSafe; - -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ch.ethz.ssh2.ServerConnection; -import ch.ethz.ssh2.ServerSession; - -@ThreadSafe -public class IOThread extends Thread { - - private static final Logger logger = LoggerFactory.getLogger(IOThread.class); - - private final InputStream inputStream; - private final OutputStream outputStream; - private final ServerSession servSession; - private final ServerConnection servconnection; - private String customHeader; - - - public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn){ - this.inputStream = is; - this.outputStream = os; - this.servSession = ss; - this.servconnection = conn; - super.setName(id); - logger.trace("IOThread {} created", super.getName()); - } - - public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn,String header){ - this.inputStream = is; - this.outputStream = os; - this.servSession = ss; - this.servconnection = conn; - this.customHeader = header; - super.setName(id); - logger.trace("IOThread {} created", super.getName()); - } - - @Override - public void run() { - logger.trace("thread {} started", super.getName()); - try { - if (this.customHeader!=null && !this.customHeader.equals("")){ - this.outputStream.write(this.customHeader.getBytes()); - logger.trace("adding {} header", this.customHeader); - } - IOUtils.copy(this.inputStream, this.outputStream); - } catch (Exception e) { - logger.error("inputstream -> outputstream copy error ",e); - } - logger.trace("closing server session"); - servSession.close(); - servconnection.close(); - logger.trace("thread {} is closing",super.getName()); - } -} diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java deleted file mode 100644 index 04639cb36f..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.netconf.ssh.threads; - - -import ch.ethz.ssh2.AuthenticationResult; -import ch.ethz.ssh2.PtySettings; -import ch.ethz.ssh2.ServerAuthenticationCallback; -import ch.ethz.ssh2.ServerConnection; -import ch.ethz.ssh2.ServerConnectionCallback; -import ch.ethz.ssh2.ServerSession; -import ch.ethz.ssh2.ServerSessionCallback; -import ch.ethz.ssh2.SimpleServerSessionCallback; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.concurrent.ThreadSafe; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -@ThreadSafe -public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback { - private static final Logger logger = LoggerFactory.getLogger(SocketThread.class); - - private final Socket socket; - private final InetSocketAddress clientAddress; - private ServerConnection conn = null; - private final long sessionId; - private String currentUser; - private final String remoteAddressWithPort; - private final AuthProvider authProvider; - - - public static void start(Socket socket, - InetSocketAddress clientAddress, - long sessionId, - AuthProvider authProvider) throws IOException { - Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket, clientAddress, sessionId, authProvider)); - netconf_ssh_socket_thread.setDaemon(true); - netconf_ssh_socket_thread.start(); - } - - private SocketThread(Socket socket, - InetSocketAddress clientAddress, - long sessionId, - AuthProvider authProvider) throws IOException { - - this.socket = socket; - this.clientAddress = clientAddress; - this.sessionId = sessionId; - this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", ""); - this.authProvider = authProvider; - - } - - @Override - public void run() { - conn = new ServerConnection(socket); - try { - conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf"); - } catch (Exception e) { - logger.warn("Server authentication setup failed.", e); - } - conn.setAuthenticationCallback(this); - conn.setServerConnectionCallback(this); - try { - conn.connect(); - } catch (IOException e) { - logger.error("SocketThread error ", e); - } - } - - @Override - public ServerSessionCallback acceptSession(final ServerSession session) { - SimpleServerSessionCallback cb = new SimpleServerSessionCallback() { - @Override - public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException { - return new Runnable() { - @Override - public void run() { - if (subsystem.equals("netconf")) { - IOThread netconf_ssh_input = null; - IOThread netconf_ssh_output = null; - try { - String hostName = clientAddress.getHostName(); - int portNumber = clientAddress.getPort(); - final Socket echoSocket = new Socket(hostName, portNumber); - logger.trace("echo socket created"); - - logger.trace("starting netconf_ssh_input thread"); - netconf_ssh_input = new IOThread(echoSocket.getInputStream(), ss.getStdin(), "input_thread_" + sessionId, ss, conn); - netconf_ssh_input.setDaemon(false); - netconf_ssh_input.start(); - - logger.trace("starting netconf_ssh_output thread"); - final String customHeader = "[" + currentUser + ";" + remoteAddressWithPort + ";ssh;;;;;;]\n"; - netconf_ssh_output = new IOThread(ss.getStdout(), echoSocket.getOutputStream(), "output_thread_" + sessionId, ss, conn, customHeader); - netconf_ssh_output.setDaemon(false); - netconf_ssh_output.start(); - - } catch (Exception t) { - logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t); - - try { - if (netconf_ssh_input != null) { - netconf_ssh_input.join(); - } - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - logger.error("netconf_ssh_input join error ", e1); - } - - try { - if (netconf_ssh_output != null) { - netconf_ssh_output.join(); - } - } catch (InterruptedException e2) { - Thread.currentThread().interrupt(); - logger.error("netconf_ssh_output join error ", e2); - } - } - } else { - String reason = "Only netconf subsystem is supported, requested:" + subsystem; - closeSession(ss, reason); - } - } - }; - } - - public void closeSession(ServerSession ss, String reason) { - logger.trace("Closing session - {}", reason); - try { - ss.getStdin().write(reason.getBytes()); - } catch (IOException e) { - logger.debug("Exception while closing session", e); - } - ss.close(); - } - - @Override - public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException { - return new Runnable() { - @Override - public void run() { - closeSession(ss, "PTY request not supported"); - } - }; - } - - @Override - public Runnable requestShell(final ServerSession ss) throws IOException { - return new Runnable() { - @Override - public void run() { - closeSession(ss, "Shell not supported"); - } - }; - } - }; - - return cb; - } - - @Override - public String initAuthentication(ServerConnection sc) { - logger.trace("Established connection with host {}", remoteAddressWithPort); - return "Established connection with host " + remoteAddressWithPort + "\r\n"; - } - - @Override - public String[] getRemainingAuthMethods(ServerConnection sc) { - return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD}; - } - - @Override - public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) { - return AuthenticationResult.FAILURE; - } - - @Override - public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) { - - try { - if (authProvider.authenticated(username, password)) { - currentUser = username; - logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort); - return AuthenticationResult.SUCCESS; - } - } catch (Exception e) { - logger.warn("Authentication failed due to :" + e.getLocalizedMessage()); - } - return AuthenticationResult.FAILURE; - } - - @Override - public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm, - byte[] publickey, byte[] signature) { - return AuthenticationResult.FAILURE; - } - -} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java deleted file mode 100644 index 298f91ce8d..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.netconf; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; -import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; -import org.opendaylight.controller.usermanager.IUserManager; -import org.opendaylight.controller.usermanager.UserConfig; - -import java.io.File; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetSocketAddress; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doReturn; - -// This test is intended to be verified using ssh -@Ignore -public class KeyGeneratorTest { - - @Mock - private IUserManager iUserManager; - File tempFile; - - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - doReturn(null).when(iUserManager).addLocalUser(any(UserConfig.class)); - tempFile = File.createTempFile("odltest", ".tmp"); - tempFile.deleteOnExit(); - } - - @After - public void tearDown() { - assertTrue(tempFile.delete()); - } - - @Test - public void test() throws Exception { - String pem = PEMGenerator.generateTo(tempFile); - - AuthProvider authProvider = new AuthProvider(iUserManager, pem); - InetSocketAddress inetSocketAddress = new InetSocketAddress(Inet4Address.getLoopbackAddress().getHostAddress(), 8383); - NetconfSSHServer server = NetconfSSHServer.start(1830, inetSocketAddress, authProvider); - - Thread serverThread = new Thread(server,"netconf SSH server thread"); - serverThread.start(); - serverThread.join(); - } -} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java deleted file mode 100644 index 663a0b4a82..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.netconf; - -import ch.ethz.ssh2.Connection; -import junit.framework.Assert; -import org.apache.commons.io.IOUtils; -import org.junit.Test; -import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStream; -import java.net.InetSocketAddress; - - -public class SSHServerTest { - - private static final String USER = "netconf"; - private static final String PASSWORD = "netconf"; - private static final String HOST = "127.0.0.1"; - private static final int PORT = 1830; - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383); - private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class); - private Thread sshServerThread; - - - - - public void startSSHServer() throws Exception{ - logger.info("Creating SSH server"); - StubUserManager um = new StubUserManager(USER,PASSWORD); - String pem; - try(InputStream is = getClass().getResourceAsStream("/RSA.pk")) { - pem = IOUtils.toString(is); - } - AuthProvider ap = new AuthProvider(um, pem); - NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress,ap); - sshServerThread = new Thread(server); - sshServerThread.setDaemon(true); - sshServerThread.start(); - logger.info("SSH server on"); - } - - @Test - public void connect(){ - try { - this.startSSHServer(); - Connection conn = new Connection(HOST,PORT); - Assert.assertNotNull(conn); - logger.info("connecting to SSH server"); - conn.connect(); - logger.info("authenticating ..."); - boolean isAuthenticated = conn.authenticateWithPassword(USER,PASSWORD); - Assert.assertTrue(isAuthenticated); - } catch (Exception e) { - logger.error("Error while starting SSH server.", e); - } - - } - -} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java new file mode 100644 index 0000000000..5d0c71aa62 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Sends one message when a connection is open and echoes back any received + * data to the server. Simply put, the echo client initiates the ping-pong + * traffic between the echo client and server by sending the first message to + * the server. + */ +public class EchoClient implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(EchoClient.class); + + private final ChannelHandler clientHandler; + + + public EchoClient(ChannelHandler clientHandler) { + this.clientHandler = clientHandler; + } + + public void run() { + // Configure the client. + EventLoopGroup group = new NioEventLoopGroup(); + try { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(LocalChannel.class) + .handler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(clientHandler); + } + }); + + // Start the client. + LocalAddress localAddress = new LocalAddress("foo"); + ChannelFuture f = b.connect(localAddress).sync(); + + // Wait until the connection is closed. + f.channel().closeFuture().sync(); + } catch (Exception e) { + logger.error("Error in client", e); + throw new RuntimeException("Error in client", e); + } finally { + // Shut down the event loop to terminate all threads. + logger.info("Client is shutting down"); + group.shutdownGracefully(); + } + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java new file mode 100644 index 0000000000..81182a580e --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Charsets; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handler implementation for the echo client. It initiates the ping-pong + * traffic between the echo client and server by sending the first message to + * the server. + */ +public class EchoClientHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(EchoClientHandler.class); + + private ChannelHandlerContext ctx; + + @Override + public void channelActive(ChannelHandlerContext ctx) { + checkState(this.ctx == null); + logger.info("client active"); + this.ctx = ctx; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf bb = (ByteBuf) msg; + logger.info(">{}", bb.toString(Charsets.UTF_8)); + bb.release(); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("Unexpected exception from downstream.", cause); + checkState(this.ctx.equals(ctx)); + ctx.close(); + this.ctx = null; + } + + public void write(String message) { + ByteBuf byteBuf = Unpooled.copiedBuffer(message.getBytes()); + ctx.writeAndFlush(byteBuf); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java new file mode 100644 index 0000000000..ec89d75f29 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalServerChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Echoes back any received data from a client. + */ +public class EchoServer implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(EchoServer.class); + + public void run() { + // Configure the server. + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(LocalServerChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(new EchoServerHandler()); + } + }); + + // Start the server. + LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); + ChannelFuture f = b.bind(localAddress).sync(); + + // Wait until the server socket is closed. + f.channel().closeFuture().sync(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + // Shut down all event loops to terminate all threads. + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new Thread(new EchoServer()).start(); + Thread.sleep(1000); + EchoClientHandler clientHandler = new EchoClientHandler(); + EchoClient echoClient = new EchoClient(clientHandler); + new Thread(echoClient).start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + do { + String message = reader.readLine(); + if (message == null || "exit".equalsIgnoreCase(message)) { + break; + } + logger.debug("Got '{}'", message); + clientHandler.write(message); + } while (true); + System.exit(0); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java new file mode 100644 index 0000000000..1286ec6875 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import com.google.common.base.Charsets; +import com.google.common.base.Splitter; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handler implementation for the echo server. + */ +@Sharable +public class EchoServerHandler extends ChannelInboundHandlerAdapter { + + private static final Logger logger = LoggerFactory.getLogger(EchoServerHandler.class.getName()); + private String fromLastNewLine = ""; + private final Splitter splitter = Splitter.onPattern("\r?\n"); + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + logger.debug("sleep start"); + Thread.sleep(1000); + logger.debug("sleep done"); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf byteBuf = (ByteBuf) msg; + String message = byteBuf.toString(Charsets.UTF_8); + logger.info("writing back '{}'", message); + ctx.write(msg); + fromLastNewLine += message; + for (String line : splitter.split(fromLastNewLine)) { + if ("quit".equals(line)) { + logger.info("closing server ctx"); + ctx.flush(); + ctx.close(); + break; + } + fromLastNewLine = line; // last line should be preserved + } + + // do not release byteBuf as it is handled back + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + logger.debug("flushing"); + ctx.flush(); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java new file mode 100644 index 0000000000..8f2c50278d --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import java.net.InetSocketAddress; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; + +public class ProxyServer implements Runnable { + private final ProxyHandlerFactory proxyHandlerFactory; + + public ProxyServer(ProxyHandlerFactory proxyHandlerFactory) { + this.proxyHandlerFactory = proxyHandlerFactory; + } + + public void run() { + // Configure the server. + final EventLoopGroup bossGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(proxyHandlerFactory.create(bossGroup, localAddress)); + } + }); + + // Start the server. + InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8080); + ChannelFuture f = serverBootstrap.bind(address).sync(); + + // Wait until the server socket is closed. + f.channel().closeFuture().sync(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + // Shut down all event loops to terminate all threads. + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + public static interface ProxyHandlerFactory { + ChannelHandler create(EventLoopGroup bossGroup, LocalAddress localAddress); + } + + public static void main(String[] args) { + ProxyHandlerFactory proxyHandlerFactory = new ProxyHandlerFactory() { + @Override + public ChannelHandler create(EventLoopGroup bossGroup, LocalAddress localAddress) { + return new ProxyServerHandler(bossGroup, localAddress); + } + }; + start(proxyHandlerFactory); + } + + public static void start(ProxyHandlerFactory proxyHandlerFactory) { + new Thread(new EchoServer()).start(); + new Thread(new ProxyServer(proxyHandlerFactory)).start(); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java new file mode 100644 index 0000000000..ecab21256e --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import com.google.common.base.Charsets; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProxyServerHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(ProxyServerHandler.class.getName()); + private final Bootstrap clientBootstrap; + private final LocalAddress localAddress; + + + private Channel clientChannel; + + public ProxyServerHandler(EventLoopGroup bossGroup, LocalAddress localAddress) { + clientBootstrap = new Bootstrap(); + clientBootstrap.group(bossGroup).channel(LocalChannel.class); + this.localAddress = localAddress; + } + + @Override + public void channelActive(ChannelHandlerContext remoteCtx) { + final ProxyClientHandler clientHandler = new ProxyClientHandler(remoteCtx); + clientBootstrap.handler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(clientHandler); + } + }); + ChannelFuture clientChannelFuture = clientBootstrap.connect(localAddress).awaitUninterruptibly(); + clientChannel = clientChannelFuture.channel(); + clientChannel.writeAndFlush(Unpooled.copiedBuffer("connected\n".getBytes())); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + logger.info("channelInactive - closing client connection"); + clientChannel.close(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, final Object msg) { + logger.debug("Writing to client {}", msg); + clientChannel.write(msg); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + logger.debug("flushing"); + clientChannel.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("Unexpected exception from downstream.", cause); + ctx.close(); + } +} + +class ProxyClientHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(ProxyClientHandler.class); + + private final ChannelHandlerContext remoteCtx; + + + public ProxyClientHandler(ChannelHandlerContext remoteCtx) { + this.remoteCtx = remoteCtx; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + logger.info("client active"); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ByteBuf bb = (ByteBuf) msg; + logger.info(">{}", bb.toString(Charsets.UTF_8)); + remoteCtx.write(msg); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + logger.debug("Flushing server ctx"); + remoteCtx.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("Unexpected exception from downstream", cause); + ctx.close(); + } + + // called both when local or remote connection dies + @Override + public void channelInactive(ChannelHandlerContext ctx) { + logger.debug("channelInactive() called, closing remote client ctx"); + remoteCtx.close(); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java new file mode 100644 index 0000000000..4e32e82e89 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.netty; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import io.netty.channel.nio.NioEventLoopGroup; +import org.junit.Test; +import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; +import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; +import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SSHTest { + public static final Logger logger = LoggerFactory.getLogger(SSHTest.class); + + @Test + public void test() throws Exception { + new Thread(new EchoServer(), "EchoServer").start(); + AuthProvider authProvider = mock(AuthProvider.class); + doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray(); + doReturn(true).when(authProvider).authenticated(anyString(), anyString()); + NetconfSSHServer thread = NetconfSSHServer.start(1831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup()); + Thread.sleep(2000); + logger.info("Closing socket"); + thread.close(); + thread.join(); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java new file mode 100644 index 0000000000..5e368bc566 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.netconf.ssh.authentication; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; + +import ch.ethz.ssh2.Connection; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import java.io.InputStream; +import java.net.InetSocketAddress; +import junit.framework.Assert; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.netconf.StubUserManager; +import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class SSHServerTest { + + private static final String USER = "netconf"; + private static final String PASSWORD = "netconf"; + private static final String HOST = "127.0.0.1"; + private static final int PORT = 1830; + private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383); + private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class); + private Thread sshServerThread; + + @Mock + private BundleContext mockedContext; + + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(null).when(mockedContext).createFilter(anyString()); + doNothing().when(mockedContext).addServiceListener(any(ServiceListener.class), anyString()); + doReturn(new ServiceReference[0]).when(mockedContext).getServiceReferences(anyString(), anyString()); + + logger.info("Creating SSH server"); + StubUserManager um = new StubUserManager(USER, PASSWORD); + String pem; + try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) { + pem = IOUtils.toString(is); + } + AuthProvider ap = new AuthProvider(pem, mockedContext); + ap.setNullableUserManager(um); + EventLoopGroup bossGroup = new NioEventLoopGroup(); + NetconfSSHServer server = NetconfSSHServer.start(PORT, NetconfConfigUtil.getNetconfLocalAddress(), + ap, bossGroup); + + sshServerThread = new Thread(server); + sshServerThread.setDaemon(true); + sshServerThread.start(); + logger.info("SSH server on " + PORT); + } + + @Test + public void connect() { + try { + Connection conn = new Connection(HOST, PORT); + Assert.assertNotNull(conn); + logger.info("connecting to SSH server"); + conn.connect(); + logger.info("authenticating ..."); + boolean isAuthenticated = conn.authenticateWithPassword(USER, PASSWORD); + Assert.assertTrue(isAuthenticated); + } catch (Exception e) { + logger.error("Error while starting SSH server.", e); + } + + } + +} diff --git a/opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml b/opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..324c234330 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/opendaylight/netconf/netconf-tcp/pom.xml b/opendaylight/netconf/netconf-tcp/pom.xml new file mode 100644 index 0000000000..65da6e987e --- /dev/null +++ b/opendaylight/netconf/netconf-tcp/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + org.opendaylight.controller + netconf-subsystem + 0.2.5-SNAPSHOT + ../ + + netconf-tcp + bundle + ${project.artifactId} + + + + ${project.groupId} + netconf-api + + + ${project.groupId} + netconf-util + + + commons-io + commons-io + + + org.slf4j + slf4j-api + + + org.opendaylight.yangtools + mockito-configuration + test + + + + + + + org.apache.felix + maven-bundle-plugin + 2.3.7 + + + org.opendaylight.controller.netconf.tcp.osgi.NetconfTCPActivator + com.google.common.base, io.netty.bootstrap, io.netty.channel, io.netty.channel.local, + io.netty.channel.nio, io.netty.channel.socket, io.netty.channel.socket.nio, io.netty.handler.logging, + io.netty.util.concurrent, org.opendaylight.controller.netconf.util.osgi, org.osgi.framework, org.slf4j + + + + + + + diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java new file mode 100644 index 0000000000..2e0022cb07 --- /dev/null +++ b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.tcp.netty; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import java.net.InetSocketAddress; + +public class ProxyServer implements AutoCloseable { + private final EventLoopGroup bossGroup = new NioEventLoopGroup(); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private final ChannelFuture channelFuture; + + public ProxyServer(InetSocketAddress address, final LocalAddress localAddress) { + // Configure the server. + final Bootstrap clientBootstrap = new Bootstrap(); + clientBootstrap.group(bossGroup).channel(LocalChannel.class); + + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.DEBUG)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new ProxyServerHandler(clientBootstrap, localAddress)); + } + }); + + // Start the server. + channelFuture = serverBootstrap.bind(address).syncUninterruptibly(); + } + + @Override + public void close() { + channelFuture.channel().close(); + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } +} diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java new file mode 100644 index 0000000000..fa8892853b --- /dev/null +++ b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.tcp.netty; + +import static com.google.common.base.Preconditions.checkState; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProxyServerHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(ProxyServerHandler.class.getName()); + private final Bootstrap clientBootstrap; + private final LocalAddress localAddress; + + private Channel clientChannel; + + public ProxyServerHandler(Bootstrap clientBootstrap, LocalAddress localAddress) { + this.clientBootstrap = clientBootstrap; + this.localAddress = localAddress; + } + + @Override + public void channelActive(ChannelHandlerContext remoteCtx) { + final ProxyClientHandler clientHandler = new ProxyClientHandler(remoteCtx); + clientBootstrap.handler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(clientHandler); + } + }); + ChannelFuture clientChannelFuture = clientBootstrap.connect(localAddress).awaitUninterruptibly(); + clientChannel = clientChannelFuture.channel(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + logger.trace("channelInactive - closing client channel"); + clientChannel.close(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, final Object msg) { + logger.trace("Writing to client channel"); + clientChannel.write(msg); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + logger.trace("Flushing client channel"); + clientChannel.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("Unexpected exception from downstream.", cause); + ctx.close(); + } +} + +class ProxyClientHandler extends ChannelInboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(ProxyClientHandler.class); + + private final ChannelHandlerContext remoteCtx; + private ChannelHandlerContext localCtx; + + public ProxyClientHandler(ChannelHandlerContext remoteCtx) { + this.remoteCtx = remoteCtx; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + checkState(this.localCtx == null); + logger.trace("Client channel active"); + this.localCtx = ctx; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + logger.trace("Forwarding message"); + remoteCtx.write(msg); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + logger.trace("Flushing remote ctx"); + remoteCtx.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // Close the connection when an exception is raised. + logger.warn("Unexpected exception from downstream", cause); + checkState(this.localCtx.equals(ctx)); + ctx.close(); + } + + // called both when local or remote connection dies + @Override + public void channelInactive(ChannelHandlerContext ctx) { + logger.trace("channelInactive() called, closing remote client ctx"); + remoteCtx.close(); + } + +} diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java new file mode 100644 index 0000000000..bc94e596d7 --- /dev/null +++ b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 Cisco Systems, 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.controller.netconf.tcp.osgi; + +import com.google.common.base.Optional; +import java.net.InetSocketAddress; +import org.opendaylight.controller.netconf.tcp.netty.ProxyServer; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Opens TCP port specified in config.ini, creates bridge between this port and local netconf server. + */ +public class NetconfTCPActivator implements BundleActivator { + private static final Logger logger = LoggerFactory.getLogger(NetconfTCPActivator.class); + private ProxyServer proxyServer; + + @Override + public void start(BundleContext context) { + final Optional maybeAddress = NetconfConfigUtil.extractNetconfServerAddress(context, InfixProp.tcp); + if (maybeAddress.isPresent() == false) { + logger.debug("Netconf tcp server is not configured to start"); + return; + } + InetSocketAddress address = maybeAddress.get(); + if (address.getAddress().isAnyLocalAddress()) { + logger.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " + + "Consider changing {} to 127.0.0.1", NetconfConfigUtil.getNetconfServerAddressKey(InfixProp.tcp)); + } + logger.info("Starting TCP netconf server at {}", address); + proxyServer = new ProxyServer(address, NetconfConfigUtil.getNetconfLocalAddress()); + } + + @Override + public void stop(BundleContext context) { + if (proxyServer != null) { + proxyServer.close(); + } + } +} diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml index dcbdcabbba..d9d957c663 100644 --- a/opendaylight/netconf/netconf-util/pom.xml +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -53,13 +53,14 @@ org.apache.felix maven-bundle-plugin + 2.3.7 com.google.common.base, com.google.common.collect, io.netty.channel, io.netty.util.concurrent, javax.annotation, javax.xml.namespace, javax.xml.parsers, javax.xml.transform, javax.xml.transform.dom, javax.xml.transform.stream, javax.xml.validation, javax.xml.xpath, org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.mapping.api, - org.osgi.framework, org.slf4j, org.w3c.dom, org.xml.sax + org.osgi.framework, org.slf4j, org.w3c.dom, org.xml.sax,io.netty.channel.local org.opendaylight.controller.netconf.util.* diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java index 0993b8ad0c..333fea3493 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java @@ -9,36 +9,35 @@ package org.opendaylight.controller.netconf.util.osgi; import com.google.common.base.Optional; +import io.netty.channel.local.LocalAddress; +import java.net.InetSocketAddress; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.InetSocketAddress; - public final class NetconfConfigUtil { private static final Logger logger = LoggerFactory.getLogger(NetconfConfigUtil.class); - public static final InetSocketAddress DEFAULT_NETCONF_TCP_ADDRESS - = new InetSocketAddress("127.0.0.1", 8383); - public static final InetSocketAddress DEFAULT_NETCONF_SSH_ADDRESS - = new InetSocketAddress("0.0.0.0", 1830); - private static final String PREFIX_PROP = "netconf."; private NetconfConfigUtil() { } - private enum InfixProp { + public enum InfixProp { tcp, ssh } private static final String PORT_SUFFIX_PROP = ".port"; private static final String ADDRESS_SUFFIX_PROP = ".address"; - private static final String CLIENT_PROP = ".client"; private static final String PRIVATE_KEY_PATH_PROP = ".pk.path"; private static final String CONNECTION_TIMEOUT_MILLIS_PROP = "connectionTimeoutMillis"; private static final long DEFAULT_TIMEOUT_MILLIS = 5000; + private static final LocalAddress netconfLocalAddress = new LocalAddress("netconf"); + + public static LocalAddress getNetconfLocalAddress() { + return netconfLocalAddress; + } public static long extractTimeoutMillis(final BundleContext bundleContext) { final String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP; @@ -54,22 +53,6 @@ public final class NetconfConfigUtil { } } - public static InetSocketAddress extractTCPNetconfServerAddress(final BundleContext context, final InetSocketAddress defaultAddress) { - final Optional extracted = extractNetconfServerAddress(context, InfixProp.tcp); - final InetSocketAddress netconfTcpAddress = getNetconfAddress(defaultAddress, extracted, InfixProp.tcp); - logger.debug("Using {} as netconf tcp address", netconfTcpAddress); - if (netconfTcpAddress.getAddress().isAnyLocalAddress()) { - logger.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " + - "Consider changing {} to 127.0.0.1", PREFIX_PROP + InfixProp.tcp + ADDRESS_SUFFIX_PROP); - } - return netconfTcpAddress; - } - - public static InetSocketAddress extractTCPNetconfClientAddress(final BundleContext context, final InetSocketAddress defaultAddress) { - final Optional extracted = extractNetconfClientAddress(context, InfixProp.tcp); - return getNetconfAddress(defaultAddress, extracted, InfixProp.tcp); - } - /** * Get extracted address or default. * @@ -93,15 +76,12 @@ public final class NetconfConfigUtil { return inetSocketAddress; } - public static InetSocketAddress extractSSHNetconfAddress(final BundleContext context, final InetSocketAddress defaultAddress) { - Optional extractedAddress = extractNetconfServerAddress(context, InfixProp.ssh); - InetSocketAddress netconfSSHAddress = getNetconfAddress(defaultAddress, extractedAddress, InfixProp.ssh); - logger.debug("Using {} as netconf SSH address", netconfSSHAddress); - return netconfSSHAddress; + public static String getPrivateKeyPath(final BundleContext context) { + return getPropertyValue(context, getPrivateKeyKey()); } - public static String getPrivateKeyPath(final BundleContext context) { - return getPropertyValue(context, PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP); + public static String getPrivateKeyKey() { + return PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP; } private static String getPropertyValue(final BundleContext context, final String propertyName) { @@ -112,16 +92,20 @@ public final class NetconfConfigUtil { return propertyValue; } + public static String getNetconfServerAddressKey(InfixProp infixProp) { + return PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP; + } + /** * @param context from which properties are being read. * @param infixProp either tcp or ssh * @return value if address and port are present and valid, Optional.absent otherwise. * @throws IllegalStateException if address or port are invalid, or configuration is missing */ - private static Optional extractNetconfServerAddress(final BundleContext context, + public static Optional extractNetconfServerAddress(final BundleContext context, final InfixProp infixProp) { - final Optional address = getProperty(context, PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP); + final Optional address = getProperty(context, getNetconfServerAddressKey(infixProp)); final Optional port = getProperty(context, PREFIX_PROP + infixProp + PORT_SUFFIX_PROP); if (address.isPresent() && port.isPresent()) { @@ -140,24 +124,6 @@ public final class NetconfConfigUtil { return new InetSocketAddress(address.get(), portNumber); } - private static Optional extractNetconfClientAddress(final BundleContext context, - final InfixProp infixProp) { - final Optional address = getProperty(context, - PREFIX_PROP + infixProp + CLIENT_PROP + ADDRESS_SUFFIX_PROP); - final Optional port = getProperty(context, - PREFIX_PROP + infixProp + CLIENT_PROP + PORT_SUFFIX_PROP); - - if (address.isPresent() && port.isPresent()) { - try { - return Optional.of(parseAddress(address, port)); - } catch (final RuntimeException e) { - logger.warn("Unable to parse client {} netconf address from {}:{}, fallback to server address", - infixProp, address, port, e); - } - } - return extractNetconfServerAddress(context, infixProp); - } - private static Optional getProperty(final BundleContext context, final String propKey) { String value = context.getProperty(propKey); if (value != null && value.isEmpty()) { diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index 4f87fd8626..d26fcf987e 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -27,6 +27,7 @@ netconf-mapping-api netconf-client netconf-ssh + netconf-tcp netconf-monitoring ietf-netconf-monitoring ietf-netconf-monitoring-extension @@ -99,6 +100,35 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.12 + + false + false + checkstyle-logging.xml + true + true + ${project.basedir} + **\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang + **\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/ + + + + org.opendaylight.yangtools + checkstyle-logging + ${yangtools.version} + + + + + + check + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java index 4b2badd92d..8253ac46d3 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java @@ -230,4 +230,12 @@ public class TCP extends Packet { return (BitBufferHelper.getShort(fieldValues.get(DESTPORT))); } + /** + * Get the stored checksum value of the TCP header + * @return short - the checksum + */ + public short getChecksum() { + return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM))); + } + } diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/TCPTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/TCPTest.java index 48679c33f2..3e18aedfdb 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/TCPTest.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/TCPTest.java @@ -104,4 +104,13 @@ public class TCPTest { Assert.assertTrue(urgentPointer[1] == 10); } + + @Test + public void testGetChecksum() { + TCP tcp = new TCP(); + byte[] udpChecksum = { 0, -56 }; + tcp.hdrFieldsMap.put("Checksum", udpChecksum); + short checksum = tcp.getChecksum(); + Assert.assertTrue(checksum == 200); + } }