Merge "Added trasaction capabilities."
authorEd Warnicke <eaw@cisco.com>
Fri, 8 Nov 2013 10:41:22 +0000 (10:41 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 8 Nov 2013 10:41:22 +0000 (10:41 +0000)
26 files changed:
opendaylight/commons/checkstyle/src/main/resources/controller/checkstyle.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataTransactionImpl.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChange.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeEvent.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractDataModification.java
opendaylight/netconf/netconf-util/pom.xml
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/AuthenticationHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/Invoker.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelInputStream.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelOutputStream.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocket.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocketException.java [new file with mode: 0644]
opendaylight/netconf/pom.xml
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSubnetsNorthbound.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java

index 57f169db730806a837d93eb73ffe245b61f0eb25..2b7462ac681665e5714fd5550ea1c6c7fe56e7da 100644 (file)
@@ -19,6 +19,7 @@
        <module name="AvoidStarImport"/>\r
        <module name="UpperEll"/>\r
        <module name="EmptyStatement"/>\r
+       <module name="EqualsHashCode"/>\r
     </module>\r
 \r
 </module>\r
index f7b2a01eb845ab7983f39c3fad5bc6e30843559e..8ff25c35def4f93e9b0d1a98b81f21e5673b96bc 100644 (file)
         <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
         <version>7.0.42</version>
       </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller.thirdparty</groupId>
+        <artifactId>ganymed</artifactId>
+        <version>1.0-SNAPSHOT</version>
+      </dependency>
       <!-- yang model dependencies -->
       <dependency>
        <groupId>org.opendaylight.yangtools.model</groupId>
index 9b07f75c6dc315ce97cfec342599f4136b126e9d..a0d7162b3f524630ecbe944eebe0551abfa07086 100644 (file)
            <version>2.4</version>
          </dependency>
 
-           <dependency>
+         <dependency>
           <groupId>org.opendaylight.yangtools.thirdparty</groupId>
           <artifactId>antlr4-runtime-osgi-nohead</artifactId>
           <version>4.0</version>
           <artifactId>yang-model-api</artifactId>
          </dependency>
 
-          <dependency>
-           <groupId>org.opendaylight.yangtools.model</groupId>
-           <artifactId>yang-ext</artifactId>
-          </dependency>
+         <dependency>
+          <groupId>org.opendaylight.yangtools.model</groupId>
+          <artifactId>yang-ext</artifactId>
+         </dependency>
 
+        <dependency>
+         <groupId>org.opendaylight.controller.thirdparty</groupId>
+         <artifactId>ganymed</artifactId>
+        </dependency>
       </dependencies>
     </profile>
   </profiles>
index 9cb9caf7c1c3d22c35263de52fb8debc91e87bc6..c970fc5e920db0cfbfcd8a277ec9bcaa8a109fb6 100644 (file)
@@ -22,6 +22,7 @@ public class DataTransactionImpl extends AbstractDataModification<InstanceIdenti
     final DataBrokerImpl broker;
 
     public DataTransactionImpl(DataBrokerImpl dataBroker) {
+        super(dataBroker);
         identifier = new Object();
         broker = dataBroker;
         status = TransactionStatus.NEW;
index 55565252a2b7a408579f46e05b9794cbe707d81a..30a607d95b6a025619e836dd26097707704ecae3 100644 (file)
@@ -49,6 +49,8 @@ public interface DataChange<P/* extends Path<P> */, D> {
      */
     Map<P, D> getUpdatedConfigurationData();
 
+
+
     /**
      * Returns a set of paths of removed objects.
      * 
@@ -82,33 +84,4 @@ public interface DataChange<P/* extends Path<P> */, D> {
      * @return map of paths and original state of updated and removed objectd.
      */
     Map<P, D> getOriginalOperationalData();
-
-    /**
-     * Returns a original subtree of data, which starts at the path
-     * where listener was registered.
-     * 
-     */
-    D getOriginalConfigurationSubtree();
-
-    /**
-     * Returns a original subtree of data, which starts at the path
-     * where listener was registered.
-     * 
-     */
-    D getOriginalOperationalSubtree();
-
-    /**
-     * Returns a new subtree of data, which starts at the path
-     * where listener was registered.
-     * 
-     */
-    D getUpdatedConfigurationSubtree();
-
-    /**
-     * Returns a new subtree of data, which starts at the path
-     * where listener was registered.
-     * 
-     */
-    D getUpdatedOperationalSubtree();
-
 }
index 590541374504489f06785732fb93075b3d3aa883..144a81b2566f549c56e7cbb00bb0e9567db644f2 100644 (file)
@@ -11,4 +11,17 @@ import org.opendaylight.yangtools.concepts.Immutable;
 
 public interface DataChangeEvent<P,D> extends DataChange<P, D>, Immutable {
 
+    /**
+     * Returns a new subtree of data, which starts at the path
+     * where listener was registered.
+     * 
+     */
+    D getUpdatedConfigurationSubtree();
+
+    /**
+     * Returns a new subtree of data, which starts at the path
+     * where listener was registered.
+     * 
+     */
+    D getUpdatedOperationalSubtree();
 }
index 1ab7c34589bec6cbed7ce375e3330c591add348b..d059766dea71bee37fb8fa26a051c52b2e044cfa 100644 (file)
@@ -16,7 +16,7 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 // import org.opendaylight.yangtools.concepts.Path;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 
-public interface DataModification<P/* extends Path<P> */, D> extends DataReader<P, D> {
+public interface DataModification<P/* extends Path<P> */, D> extends DataChange<P, D>, DataReader<P, D> {
 
     /**
      * Returns transaction identifier
@@ -27,21 +27,29 @@ public interface DataModification<P/* extends Path<P> */, D> extends DataReader<
 
     TransactionStatus getStatus();
 
+    /**
+     * 
+     * Use {@link #putOperationalData(Object, Object)} instead.
+     * 
+     * @param path
+     * @param data
+     */
     void putRuntimeData(P path, D data);
 
+    void putOperationalData(P path, D data);
+
     void putConfigurationData(P path, D data);
 
+    /**
+     * Use {@link #removeOperationalData(Object)}
+     * 
+     * @param path
+     */
     void removeRuntimeData(P path);
 
-    void removeConfigurationData(P path);
-
-    public Map<P, D> getUpdatedConfigurationData();
+    void removeOperationalData(P path);
 
-    public Map<P, D> getUpdatedOperationalData();
-
-    public Set<P> getRemovedConfigurationData();
-
-    public Set<P> getRemovedOperationalData();
+    void removeConfigurationData(P path);
 
     /**
      * Initiates a two-phase commit of modification.
index 5d76717ab6ab0d5128bb2eb578f05bda48120a7f..c335b75e15981989078fa862ad31b88dd70d4e04 100644 (file)
@@ -5,68 +5,109 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
 import org.opendaylight.yangtools.concepts.Path;
 
 import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW;
 
-public abstract class AbstractDataModification<P /*extends Path<P>*/, D> implements DataModification<P, D> {
+public abstract class AbstractDataModification<P /* extends Path<P> */, D> implements DataModification<P, D> {
 
-    private final Map<P, D> configurationUpdate;
-    private final Map<P, D> operationalUpdate;
+    private final ConcurrentMap<P, D> operationalOriginal;
+    private final ConcurrentMap<P, D> configurationOriginal;
 
-    private final Set<P> configurationRemove;
-    private final Set<P> operationalRemove;
+    private final ConcurrentMap<P, D> operationalCreated;
+    private final ConcurrentMap<P, D> configurationCreated;
 
+    private final ConcurrentMap<P, D> configurationUpdate;
+    private final ConcurrentMap<P, D> operationalUpdate;
+
+    private final ConcurrentMap<P, P> configurationRemove;
+    private final ConcurrentMap<P, P> operationalRemove;
+
+    private final Map<P, D> unmodifiable_configurationOriginal;
+    private final Map<P, D> unmodifiable_operationalOriginal;
+    private final Map<P, D> unmodifiable_configurationCreated;
+    private final Map<P, D> unmodifiable_operationalCreated;
     private final Map<P, D> unmodifiable_configurationUpdate;
     private final Map<P, D> unmodifiable_operationalUpdate;
     private final Set<P> unmodifiable_configurationRemove;
     private final Set<P> unmodifiable_OperationalRemove;
+    private DataReader<P, D> reader;
+
+    public AbstractDataModification(DataReader<P, D> reader) {
+        this.reader = reader;
+        this.configurationUpdate = new ConcurrentHashMap<>();
+        this.operationalUpdate = new ConcurrentHashMap<>();
+        this.configurationRemove = new ConcurrentHashMap<>();
+        this.operationalRemove = new ConcurrentHashMap<>();
 
-    public AbstractDataModification(Map<P, D> configurationUpdate, Map<P, D> operationalUpdate,
-            Set<P> configurationRemove, Set<P> operationalRemove) {
-        this.configurationUpdate = configurationUpdate;
-        this.operationalUpdate = operationalUpdate;
-        this.configurationRemove = configurationRemove;
-        this.operationalRemove = operationalRemove;
+        this.configurationOriginal = new ConcurrentHashMap<>();
+        this.operationalOriginal = new ConcurrentHashMap<>();
 
+        this.configurationCreated = new ConcurrentHashMap<>();
+        this.operationalCreated = new ConcurrentHashMap<>();
+
+        unmodifiable_configurationOriginal = Collections.unmodifiableMap(configurationOriginal);
+        unmodifiable_operationalOriginal = Collections.unmodifiableMap(operationalOriginal);
+        unmodifiable_configurationCreated = Collections.unmodifiableMap(configurationCreated);
+        unmodifiable_operationalCreated = Collections.unmodifiableMap(operationalCreated);
         unmodifiable_configurationUpdate = Collections.unmodifiableMap(configurationUpdate);
         unmodifiable_operationalUpdate = Collections.unmodifiableMap(operationalUpdate);
-        unmodifiable_configurationRemove = Collections.unmodifiableSet(configurationRemove);
-        unmodifiable_OperationalRemove = Collections.unmodifiableSet(operationalRemove);
-    }
+        unmodifiable_configurationRemove = Collections.unmodifiableSet(configurationRemove.keySet());
+        unmodifiable_OperationalRemove = Collections.unmodifiableSet(operationalRemove.keySet());
 
-    public AbstractDataModification() {
-        this(new HashMap<P, D>(), new HashMap<P, D>(), new HashSet<P>(), new HashSet<P>());
     }
 
     @Override
     public final void putConfigurationData(P path, D data) {
         checkMutable();
+
+        if (!hasConfigurationOriginal(path)) {
+            configurationCreated.put(path, data);
+        }
+
         configurationUpdate.put(path, data);
         configurationRemove.remove(path);
     }
 
     @Override
-    public final void putRuntimeData(P path, D data) {
+    public final void putOperationalData(P path, D data) {
         checkMutable();
+        if (!hasOperationalOriginal(path)) {
+            operationalCreated.put(path, data);
+        }
         operationalUpdate.put(path, data);
         operationalRemove.remove(path);
     }
 
     @Override
-    public final void removeRuntimeData(P path) {
+    public final void putRuntimeData(P path, D data) {
+        putRuntimeData(path, data);
+    }
+
+    @Override
+    public final void removeOperationalData(P path) {
         checkMutable();
+        hasOperationalOriginal(path);
         operationalUpdate.remove(path);
-        operationalRemove.add(path);
+        operationalRemove.put(path, path);
+    }
+
+    @Override
+    public final void removeRuntimeData(P path) {
+        removeOperationalData(path);
     }
 
     @Override
     public final void removeConfigurationData(P path) {
         checkMutable();
+        hasConfigurationOriginal(path);
         configurationUpdate.remove(path);
-        configurationRemove.add(path);
+        configurationRemove.put(path, path);
     }
 
     private final void checkMutable() {
@@ -75,24 +116,77 @@ public abstract class AbstractDataModification<P /*extends Path<P>*/, D> impleme
     }
 
     @Override
-    public Map<P, D> getUpdatedConfigurationData() {
+    public final Map<P, D> getUpdatedConfigurationData() {
 
         return unmodifiable_configurationUpdate;
     }
 
     @Override
-    public Map<P, D> getUpdatedOperationalData() {
+    public final Map<P, D> getUpdatedOperationalData() {
         return unmodifiable_operationalUpdate;
     }
 
     @Override
-    public Set<P> getRemovedConfigurationData() {
+    public final Set<P> getRemovedConfigurationData() {
         return unmodifiable_configurationRemove;
     }
 
     @Override
-    public Set<P> getRemovedOperationalData() {
+    public final Set<P> getRemovedOperationalData() {
         return unmodifiable_OperationalRemove;
     }
 
+    @Override
+    public Map<P, D> getCreatedConfigurationData() {
+        return unmodifiable_configurationCreated;
+    }
+
+    @Override
+    public Map<P, D> getCreatedOperationalData() {
+        return unmodifiable_operationalCreated;
+    }
+
+    @Override
+    public Map<P, D> getOriginalConfigurationData() {
+        return unmodifiable_configurationOriginal;
+    }
+
+    @Override
+    public Map<P, D> getOriginalOperationalData() {
+        return unmodifiable_operationalOriginal;
+    }
+
+    @Override
+    public D readOperationalData(P path) {
+        return reader.readOperationalData(path);
+    }
+
+    @Override
+    public D readConfigurationData(P path) {
+        return reader.readConfigurationData(path);
+    }
+
+    private boolean hasConfigurationOriginal(P path) {
+        if (configurationOriginal.containsKey(path)) {
+            return true;
+        }
+        D data = reader.readConfigurationData(path);
+        if (data != null) {
+            configurationOriginal.putIfAbsent(path, data);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean hasOperationalOriginal(P path) {
+        if (operationalOriginal.containsKey(path)) {
+            return true;
+        }
+        D data = reader.readConfigurationData(path);
+        if (data != null) {
+            operationalOriginal.putIfAbsent(path, data);
+            return true;
+        }
+        return false;
+    }
 }
index 20603c477462ac31e38ee00d75b15ddd91f48e36..d6bf62413a569949da0ca09de44d5678f3c9a4da 100644 (file)
             <artifactId>netty-handler</artifactId>
             <version>${netconf.netty.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>ganymed</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
@@ -72,6 +76,7 @@
                             org.opendaylight.controller.config.stat,
                             com.google.common.base,
                             com.google.common.collect,
+                            ch.ethz.ssh2,
                             io.netty.buffer,
                             io.netty.channel,
                             io.netty.channel.socket,
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java
new file mode 100644 (file)
index 0000000..b911989
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.util.handler.ssh;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import java.io.IOException;
+import java.net.SocketAddress;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
+import org.opendaylight.controller.netconf.util.handler.ssh.client.SshClient;
+import org.opendaylight.controller.netconf.util.handler.ssh.client.SshClientAdapter;
+import org.opendaylight.controller.netconf.util.handler.ssh.virtualsocket.VirtualSocket;
+
+/**
+ * Netty SSH handler class. Acts as interface between Netty and SSH library. All standard Netty message handling
+ * stops at instance of this class. All downstream events are handed of to wrapped {@link org.opendaylight.controller.netconf.util.handler.ssh.client.SshClientAdapter};
+ */
+public class SshHandler extends ChannelOutboundHandlerAdapter {
+    private final VirtualSocket virtualSocket = new VirtualSocket();
+    private final SshClientAdapter sshClientAdapter;
+
+    public SshHandler(AuthenticationHandler authenticationHandler, Invoker invoker) throws IOException {
+        SshClient sshClient = new SshClient(virtualSocket, authenticationHandler);
+        this.sshClientAdapter = new SshClientAdapter(sshClient, invoker);
+    }
+
+    @Override
+    public void handlerAdded(ChannelHandlerContext ctx){
+        if (ctx.channel().pipeline().get("socket") == null) {
+            ctx.channel().pipeline().addFirst("socket", virtualSocket);
+        }
+    }
+
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        if (ctx.channel().pipeline().get("socket") != null) {
+            ctx.channel().pipeline().remove("socket");
+        }
+    }
+
+    @Override
+    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+        this.sshClientAdapter.write((String) msg);
+    }
+
+    @Override
+    public void connect(final ChannelHandlerContext ctx,
+                        SocketAddress remoteAddress,
+                        SocketAddress localAddress,
+                        ChannelPromise promise) throws Exception {
+        ctx.connect(remoteAddress, localAddress, promise);
+
+        promise.addListener(new ChannelFutureListener() {
+            public void operationComplete(ChannelFuture channelFuture) throws Exception {
+                sshClientAdapter.start(ctx);
+            }}
+        );
+    }
+
+    @Override
+    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+        sshClientAdapter.stop(promise);
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/AuthenticationHandler.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/AuthenticationHandler.java
new file mode 100644 (file)
index 0000000..a0e82f8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.util.handler.ssh.authentication;
+
+import ch.ethz.ssh2.Connection;
+
+import java.io.IOException;
+
+/**
+ * Class providing authentication facility to SSH handler.
+ */
+public abstract class AuthenticationHandler {
+    public abstract void authenticate(Connection connection) throws IOException;
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java
new file mode 100644 (file)
index 0000000..bb0d378
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.util.handler.ssh.authentication;
+
+import ch.ethz.ssh2.Connection;
+
+import java.io.IOException;
+
+/**
+ * Class Providing username/password authentication option to {@link org.opendaylight.controller.netconf.util.handler.ssh.SshHandler}
+ */
+public class LoginPassword extends AuthenticationHandler {
+    private final String username;
+    private final String password;
+
+    public LoginPassword(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    @Override
+    public void authenticate(Connection connection) throws IOException {
+        boolean isAuthenticated = connection.authenticateWithPassword(username, password);
+
+        if (isAuthenticated == false) throw new IOException("Authentication failed.");
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/Invoker.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/Invoker.java
new file mode 100644 (file)
index 0000000..12d1129
--- /dev/null
@@ -0,0 +1,36 @@
+package org.opendaylight.controller.netconf.util.handler.ssh.client;
+
+import java.io.IOException;
+
+/**
+ * Abstract class providing mechanism of invoking various SSH level services.
+ * Class is not allowed to be extended, as it provides its own implementations via instance initiators.
+ */
+public abstract class Invoker {
+    private boolean invoked = false;
+
+    private Invoker(){}
+
+    protected boolean isInvoked() {
+        return invoked;
+    }
+
+    abstract void invoke(SshSession session) throws IOException;
+
+    /**
+     * Invoker implementation to invokes subsystem SSH service.
+     *
+     * @param subsystem
+     * @return
+     */
+    public static Invoker subsystem(final String subsystem) {
+        return new Invoker() {
+            @Override
+            void invoke(SshSession session) throws IOException {
+                if (isInvoked() == true) throw new IllegalStateException("Already invoked.");
+
+                session.startSubSystem(subsystem);
+            }
+        };
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java
new file mode 100644 (file)
index 0000000..c43aa6f
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.util.handler.ssh.client;
+
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import ch.ethz.ssh2.channel.Channel;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.virtualsocket.VirtualSocket;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Wrapper class around GANYMED SSH java library.
+ */
+public class SshClient {
+    private final VirtualSocket socket;
+    private final Map<Integer, SshSession> openSessions = new HashMap();
+    private final AuthenticationHandler authenticationHandler;
+    private Connection connection;
+
+    public SshClient(VirtualSocket socket,
+                     AuthenticationHandler authenticationHandler) throws IOException {
+        this.socket = socket;
+        this.authenticationHandler = authenticationHandler;
+    }
+
+    public SshSession openSession() throws IOException {
+        if(connection == null) connect();
+
+        Session session =  connection.openSession();
+        SshSession sshSession = new SshSession(session);
+        openSessions.put(openSessions.size(), sshSession);
+
+        return sshSession;
+    }
+
+    private void connect() throws IOException {
+        connection = new Connection(socket);
+        connection.connect();
+        authenticationHandler.authenticate(connection);
+    }
+
+    public void closeSession(SshSession session) {
+        if(   session.getState() == Channel.STATE_OPEN
+           || session.getState() == Channel.STATE_OPENING) {
+            session.session.close();
+        }
+    }
+
+    public void close() {
+        for(SshSession session : openSessions.values()) closeSession(session);
+
+        openSessions.clear();
+
+        if(connection != null) connection.close();
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java
new file mode 100644 (file)
index 0000000..a50462e
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.util.handler.ssh.client;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.opendaylight.controller.netconf.util.handler.ssh.virtualsocket.VirtualSocketException;
+
+/**
+ * Worker thread class. Handles all downstream and upstream events in SSH Netty pipeline.
+ */
+public class SshClientAdapter implements Runnable {
+    private final SshClient sshClient;
+    private final Invoker invoker;
+
+    private SshSession session;
+    private InputStream stdOut;
+    private InputStream stdErr;
+    private OutputStream stdIn;
+
+    private ChannelHandlerContext ctx;
+    private ChannelPromise disconnectPromise;
+
+    private final AtomicBoolean stopRequested = new AtomicBoolean(false);
+
+    private final Object lock = new Object();
+
+    public SshClientAdapter(SshClient sshClient,
+                            Invoker invoker) {
+        this.sshClient = sshClient;
+        this.invoker = invoker;
+    }
+
+    public void run() {
+        try {
+            session = sshClient.openSession();
+            invoker.invoke(session);
+
+            stdOut = session.getStdout();
+            stdErr = session.getStderr();
+
+            synchronized(lock) {
+                stdIn = session.getStdin();
+            }
+
+            while (stopRequested.get() == false) {
+                byte[] readBuff = new byte[1024];
+                int c = stdOut.read(readBuff);
+
+                byte[] tranBuff = new byte[c];
+                System.arraycopy(readBuff, 0, tranBuff, 0, c);
+
+                ByteBuf byteBuf = Unpooled.buffer(c);
+                byteBuf.writeBytes(tranBuff);
+                ctx.fireChannelRead(byteBuf);
+            }
+
+        } catch (VirtualSocketException e) {
+            // Netty closed connection prematurely.
+            // Just pass and move on.
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            sshClient.close();
+
+            synchronized (lock) {
+                if(disconnectPromise != null) ctx.disconnect(disconnectPromise);
+            }
+        }
+    }
+
+    // TODO: needs rework to match netconf framer API.
+    public void write(String message) throws IOException {
+        synchronized (lock) {
+            if (stdIn == null) throw new IllegalStateException("StdIn not available");
+        }
+        stdIn.write(message.getBytes());
+        stdIn.flush();
+    }
+
+    public void stop(ChannelPromise promise) {
+        synchronized (lock) {
+            stopRequested.set(true);
+            disconnectPromise = promise;
+        }
+    }
+
+    public void start(ChannelHandlerContext ctx) {
+        if(this.ctx != null) return; // context is already associated.
+
+        this.ctx = ctx;
+        new Thread(this).start();
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshSession.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshSession.java
new file mode 100644 (file)
index 0000000..df400aa
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.util.handler.ssh.client;
+
+import ch.ethz.ssh2.Session;
+import ch.ethz.ssh2.StreamGobbler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Wrapper class for proprietary SSH sessions implementations
+ */
+public class SshSession {
+    final Session session;
+
+    public SshSession(Session session) {
+        this.session = session;
+    }
+
+    public void execCommand(String cmd) throws IOException {
+        session.execCommand(cmd);
+    }
+
+    public void execCommand(String cmd, String charsetName) throws IOException {
+        session.execCommand(cmd, charsetName);
+    }
+
+    public void startShell() throws IOException {
+        session.startShell();
+    }
+
+    public void startSubSystem(String name) throws IOException {
+        session.startSubSystem(name);
+    }
+
+    public int getState() {
+        return session.getState();
+    }
+
+    public InputStream getStdout() {
+        return new StreamGobbler(session.getStdout());
+    }
+
+    public InputStream getStderr() {
+        return session.getStderr();
+    }
+
+    public OutputStream getStdin() {
+        return session.getStdin();
+    }
+
+    public int waitUntilDataAvailable(long timeout) throws IOException {
+        return session.waitUntilDataAvailable(timeout);
+    }
+
+    public int waitForCondition(int condition_set, long timeout) {
+        return session.waitForCondition(condition_set, timeout);
+    }
+
+    public Integer getExitStatus() {
+        return session.getExitStatus();
+    }
+
+    public String getExitSignal() {
+        return session.getExitSignal();
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelInputStream.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelInputStream.java
new file mode 100644 (file)
index 0000000..07c81b0
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.util.handler.ssh.virtualsocket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class provides {@link InputStream} functionality to users of virtual socket.
+ */
+public class ChannelInputStream extends InputStream implements ChannelInboundHandler {
+    private final Object lock = new Object();
+    private final ByteBuf bb = Unpooled.buffer();
+
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        if (b == null) {
+            throw new NullPointerException();
+        } else if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return 0;
+        }
+
+        int bytesRead = 1;
+        synchronized (lock) {
+            int c = read();
+
+            b[off] = (byte)c;
+
+            if(this.bb.readableBytes() == 0) return bytesRead;
+
+            int ltr = len-1;
+            ltr = (ltr <= bb.readableBytes()) ? ltr : bb.readableBytes();
+
+            bb.readBytes(b, 1, ltr);
+            bytesRead += ltr;
+        }
+        return bytesRead;
+    }
+
+    @Override
+    public int read() throws IOException {
+        synchronized (lock) {
+            while (this.bb.readableBytes() == 0) {
+                try {
+                    lock.wait();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return this.bb.readByte() & 0xFF;
+        }
+    }
+
+    @Override
+    public int available() throws IOException {
+        synchronized (lock) {
+            return this.bb.readableBytes();
+        }
+    }
+
+    public void channelRegistered(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelRegistered();
+    }
+
+    public void channelUnregistered(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelUnregistered();
+    }
+
+    public void channelActive(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelActive();
+    }
+
+    public void channelInactive(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelInactive();
+    }
+
+    public void channelRead(ChannelHandlerContext ctx, Object o)
+            throws Exception {
+        synchronized(lock) {
+            this.bb.discardReadBytes();
+            this.bb.writeBytes((ByteBuf) o);
+            lock.notifyAll();
+        }
+    }
+
+    public void channelReadComplete(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelReadComplete();
+    }
+
+    public void userEventTriggered(ChannelHandlerContext ctx, Object o)
+            throws Exception {
+        ctx.fireUserEventTriggered(o);
+    }
+
+    public void channelWritabilityChanged(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.fireChannelWritabilityChanged();
+    }
+
+    public void handlerAdded(ChannelHandlerContext ctx)
+            throws Exception {
+    }
+
+    public void handlerRemoved(ChannelHandlerContext ctx)
+            throws Exception {
+    }
+
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable)
+            throws Exception {
+        ctx.fireExceptionCaught(throwable);
+    }
+}
+
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelOutputStream.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/ChannelOutputStream.java
new file mode 100644 (file)
index 0000000..b1314a6
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.util.handler.ssh.virtualsocket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandler;
+import io.netty.channel.ChannelPromise;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketAddress;
+
+/**
+ * Class provides {@link OutputStream) functionality to users of virtual socket.
+ */
+public class ChannelOutputStream extends OutputStream implements ChannelOutboundHandler {
+    private final Object lock = new Object();
+    private ByteBuf buff = Unpooled.buffer();
+    private ChannelHandlerContext ctx;
+
+    @Override
+    public void flush() throws IOException {
+        synchronized(lock) {
+            ctx.writeAndFlush(buff).awaitUninterruptibly();
+            buff = Unpooled.buffer();
+        }
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        synchronized(lock) {
+            buff.writeByte(b);
+        }
+    }
+
+    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
+                     ChannelPromise promise) throws Exception {
+        ctx.bind(localAddress, promise);
+    }
+
+    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
+                        SocketAddress localAddress, ChannelPromise promise)
+            throws Exception {
+        this.ctx = ctx;
+        ctx.connect(remoteAddress, localAddress, promise);
+    }
+
+    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
+            throws Exception {
+        ctx.disconnect(promise);
+    }
+
+    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
+            throws Exception {
+        ctx.close(promise);
+    }
+
+    public void deregister(ChannelHandlerContext ctx, ChannelPromise channelPromise)
+            throws Exception {
+        ctx.deregister(channelPromise);
+    }
+
+    public void read(ChannelHandlerContext ctx)
+            throws Exception {
+        ctx.read();
+    }
+
+    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
+            throws Exception {
+        // pass
+    }
+
+    public void flush(ChannelHandlerContext ctx)
+            throws Exception {
+        // pass
+    }
+
+    public void handlerAdded(ChannelHandlerContext ctx)
+            throws Exception {
+    }
+
+    public void handlerRemoved(ChannelHandlerContext ctx)
+            throws Exception {
+    }
+
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
+            throws Exception {
+        ctx.fireExceptionCaught(cause);
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocket.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocket.java
new file mode 100644 (file)
index 0000000..1011ca1
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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.util.handler.ssh.virtualsocket;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+
+/**
+ * Handler class providing Socket functionality to OIO client application. By using VirtualSocket user can
+ * use OIO application in asynchronous environment and NIO EventLoop. Using VirtualSocket OIO applications
+ * are able to use full potential of NIO environment.
+ */
+public class VirtualSocket extends Socket implements ChannelHandler {
+    private final ChannelInputStream chis = new ChannelInputStream();
+    private final ChannelOutputStream chos = new ChannelOutputStream();
+    private ChannelHandlerContext ctx;
+
+
+    public InputStream getInputStream() {
+        return this.chis;
+    }
+
+    public OutputStream getOutputStream() {
+        return this.chos;
+    }
+
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        this.ctx = ctx;
+
+        if (ctx.channel().pipeline().get("outputStream") == null) {
+            ctx.channel().pipeline().addFirst("outputStream", chos);
+        }
+
+        if (ctx.channel().pipeline().get("inputStream") == null) {
+            ctx.channel().pipeline().addFirst("inputStream", chis);
+        }
+    }
+
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        if (ctx.channel().pipeline().get("outputStream") != null) {
+            ctx.channel().pipeline().remove("outputStream");
+        }
+
+        if (ctx.channel().pipeline().get("inputStream") != null) {
+            ctx.channel().pipeline().remove("inputStream");
+        }
+    }
+
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception {
+        ctx.fireExceptionCaught(throwable);
+    }
+
+    public VirtualSocket() {super();}
+
+    @Override
+    public void connect(SocketAddress endpoint) throws IOException {}
+
+    @Override
+    public void connect(SocketAddress endpoint, int timeout) throws IOException {}
+
+    @Override
+    public void bind(SocketAddress bindpoint) throws IOException {}
+
+    @Override
+    public InetAddress getInetAddress() {
+        InetSocketAddress isa = getInetSocketAddress();
+
+        if (isa == null) throw new VirtualSocketException();
+
+        return getInetSocketAddress().getAddress();
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {return null;}
+
+    @Override
+    public int getPort() {
+        return getInetSocketAddress().getPort();
+    }
+
+    private InetSocketAddress getInetSocketAddress() {
+        return (InetSocketAddress)getRemoteSocketAddress();
+    }
+
+    @Override
+    public int getLocalPort() {return -1;}
+
+    @Override
+    public SocketAddress getRemoteSocketAddress() {
+        return this.ctx.channel().remoteAddress();
+    }
+
+    @Override
+    public SocketAddress getLocalSocketAddress() {
+        return this.ctx.channel().localAddress();
+    }
+
+    @Override
+    public SocketChannel getChannel() {return null;}
+
+    @Override
+    public void setTcpNoDelay(boolean on) throws SocketException {}
+
+    @Override
+    public boolean getTcpNoDelay() throws SocketException {return false;}
+
+    @Override
+    public void setSoLinger(boolean on, int linger) throws SocketException {}
+
+    @Override
+    public int getSoLinger() throws SocketException {return -1;}
+
+    @Override
+    public void sendUrgentData(int data) throws IOException {}
+
+    @Override
+    public void setOOBInline(boolean on) throws SocketException {}
+
+    @Override
+    public boolean getOOBInline() throws SocketException {return false;}
+
+    @Override
+    public synchronized void setSoTimeout(int timeout) throws SocketException {}
+
+    @Override
+    public synchronized int getSoTimeout() throws SocketException {return -1;}
+
+    @Override
+    public synchronized void setSendBufferSize(int size) throws SocketException {}
+
+    @Override
+    public synchronized int getSendBufferSize() throws SocketException {return -1;}
+
+    @Override
+    public synchronized void setReceiveBufferSize(int size) throws SocketException {}
+
+    @Override
+    public synchronized int getReceiveBufferSize() throws SocketException {return -1;}
+
+    @Override
+    public void setKeepAlive(boolean on) throws SocketException {}
+
+    @Override
+    public boolean getKeepAlive() throws SocketException {return false;}
+
+    @Override
+    public void setTrafficClass(int tc) throws SocketException {}
+
+    @Override
+    public int getTrafficClass() throws SocketException {return -1;}
+
+    @Override
+    public void setReuseAddress(boolean on) throws SocketException {}
+
+    @Override
+    public boolean getReuseAddress() throws SocketException {return false;}
+
+    @Override
+    public synchronized void close() throws IOException {}
+
+    @Override
+    public void shutdownInput() throws IOException {}
+
+    @Override
+    public void shutdownOutput() throws IOException {}
+
+    @Override
+    public String toString() {
+        return "Virtual socket InetAdress["+getInetAddress()+"], Port["+getPort()+"]";
+    }
+
+    @Override
+    public boolean isConnected() {return false;}
+
+    @Override
+    public boolean isBound() {return false;}
+
+    @Override
+    public boolean isClosed() {return false;}
+
+    @Override
+    public boolean isInputShutdown() {return false;}
+
+    @Override
+    public boolean isOutputShutdown() {return false;}
+
+    @Override
+    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {}
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocketException.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/virtualsocket/VirtualSocketException.java
new file mode 100644 (file)
index 0000000..46fdbb8
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.util.handler.ssh.virtualsocket;
+
+/**
+ * Exception class which provides notification about exceptional situations at the virtual socket layer.
+ */
+public class VirtualSocketException extends RuntimeException {
+}
index 8f69f8dca066a949a306bd2e98b038bdeb392a5c..5447f7f5d057c71eba0e08369986c46c31013903 100644 (file)
@@ -26,6 +26,7 @@
         <module>config-persister-impl</module>
         <module>netconf-mapping-api</module>
         <module>netconf-client</module>
+        <module>../../third-party/ganymed</module>
     </modules>
 
     <profiles>
index c6ea9421f9b9d928944ced8e7ceeb7de40a3de6c..8b1a8d6af4bb50c13e8b2166bac245fe7d41f0ce 100644 (file)
@@ -145,15 +145,16 @@ public class NeutronSubnet {
     }\r
 \r
     public boolean isEnableDHCP() {\r
-        if (enableDHCP == null)\r
+        if (enableDHCP == null) {\r
             return true;\r
+        }\r
         return enableDHCP;\r
     }\r
 \r
     public Boolean getEnableDHCP() { return enableDHCP; }\r
 \r
     public void setEnableDHCP(Boolean newValue) {\r
-            this.enableDHCP = newValue;\r
+            enableDHCP = newValue;\r
     }\r
 \r
     public String getTenantID() {\r
@@ -179,18 +180,24 @@ public class NeutronSubnet {
         Iterator<String> i = fields.iterator();\r
         while (i.hasNext()) {\r
             String s = i.next();\r
-            if (s.equals("id"))\r
+            if (s.equals("id")) {\r
                 ans.setSubnetUUID(this.getSubnetUUID());\r
-            if (s.equals("network_id"))\r
+            }\r
+            if (s.equals("network_id")) {\r
                 ans.setNetworkUUID(this.getNetworkUUID());\r
-            if (s.equals("name"))\r
+            }\r
+            if (s.equals("name")) {\r
                 ans.setName(this.getName());\r
-            if (s.equals("ip_version"))\r
+            }\r
+            if (s.equals("ip_version")) {\r
                 ans.setIpVersion(this.getIpVersion());\r
-            if (s.equals("cidr"))\r
+            }\r
+            if (s.equals("cidr")) {\r
                 ans.setCidr(this.getCidr());\r
-            if (s.equals("gateway_ip"))\r
+            }\r
+            if (s.equals("gateway_ip")) {\r
                 ans.setGatewayIP(this.getGatewayIP());\r
+            }\r
             if (s.equals("dns_nameservers")) {\r
                 List<String> nsList = new ArrayList<String>();\r
                 nsList.addAll(this.getDnsNameservers());\r
@@ -206,10 +213,12 @@ public class NeutronSubnet {
                 hRoutes.addAll(this.getHostRoutes());\r
                 ans.setHostRoutes(hRoutes);\r
             }\r
-            if (s.equals("enable_dhcp"))\r
+            if (s.equals("enable_dhcp")) {\r
                 ans.setEnableDHCP(this.getEnableDHCP());\r
-            if (s.equals("tenant_id"))\r
+            }\r
+            if (s.equals("tenant_id")) {\r
                 ans.setTenantID(this.getTenantID());\r
+            }\r
         }\r
         return ans;\r
     }\r
@@ -222,8 +231,9 @@ public class NeutronSubnet {
         try {\r
             SubnetUtils util = new SubnetUtils(cidr);\r
             SubnetInfo info = util.getInfo();\r
-            if (!info.getNetworkAddress().equals(info.getAddress()))\r
+            if (!info.getNetworkAddress().equals(info.getAddress())) {\r
                 return false;\r
+            }\r
         } catch (Exception e) {\r
             return false;\r
         }\r
@@ -238,17 +248,20 @@ public class NeutronSubnet {
         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();\r
         while (i.hasNext()) {\r
             NeutronSubnet_IPAllocationPool pool = i.next();\r
-            if (pool.contains(gatewayIP))\r
+            if (pool.contains(gatewayIP)) {\r
                 return true;\r
+            }\r
         }\r
         return false;\r
     }\r
 \r
-    public void initDefaults() {\r
-        if (enableDHCP == null)\r
+    public boolean initDefaults() {\r
+        if (enableDHCP == null) {\r
             enableDHCP = true;\r
-        if (ipVersion == null)\r
+        }\r
+        if (ipVersion == null) {\r
             ipVersion = 4;\r
+        }\r
         gatewayIPAssigned = false;\r
         dnsNameservers = new ArrayList<String>();\r
         allocationPools = new ArrayList<NeutronSubnet_IPAllocationPool>();\r
@@ -256,8 +269,9 @@ public class NeutronSubnet {
         try {\r
             SubnetUtils util = new SubnetUtils(cidr);\r
             SubnetInfo info = util.getInfo();\r
-            if (gatewayIP == null)\r
+            if (gatewayIP == null) {\r
                 gatewayIP = info.getLowAddress();\r
+            }\r
             if (allocationPools.size() < 1) {\r
                 NeutronSubnet_IPAllocationPool source =\r
                     new NeutronSubnet_IPAllocationPool(info.getLowAddress(),\r
@@ -265,7 +279,9 @@ public class NeutronSubnet {
                 allocationPools = source.splitPool(gatewayIP);\r
             }\r
         } catch (Exception e) {\r
+            return false;\r
         }\r
+        return true;\r
     }\r
 \r
     public List<NeutronPort> getPortsInSubnet() {\r
@@ -297,13 +313,15 @@ public class NeutronSubnet {
      * available allocation pools or not\r
      */\r
     public boolean isIPInUse(String ipAddress) {\r
-        if (ipAddress.equals(gatewayIP) && !gatewayIPAssigned )\r
+        if (ipAddress.equals(gatewayIP) && !gatewayIPAssigned ) {\r
             return false;\r
+        }\r
         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();\r
         while (i.hasNext()) {\r
             NeutronSubnet_IPAllocationPool pool = i.next();\r
-            if (pool.contains(ipAddress))\r
+            if (pool.contains(ipAddress)) {\r
                 return false;\r
+            }\r
         }\r
         return true;\r
     }\r
@@ -322,8 +340,9 @@ public class NeutronSubnet {
             }\r
             else\r
                 if (NeutronSubnet_IPAllocationPool.convert(pool.getPoolStart()) <\r
-                        NeutronSubnet_IPAllocationPool.convert(ans))\r
+                        NeutronSubnet_IPAllocationPool.convert(ans)) {\r
                     ans = pool.getPoolStart();\r
+                }\r
         }\r
         return ans;\r
     }\r
@@ -349,8 +368,9 @@ public class NeutronSubnet {
                 if (pool.contains(ipAddress)) {\r
                     List<NeutronSubnet_IPAllocationPool> pools = pool.splitPool(ipAddress);\r
                     newList.addAll(pools);\r
-                } else\r
+                } else {\r
                     newList.add(pool);\r
+                }\r
             }\r
         }\r
         allocationPools = newList;\r
@@ -372,20 +392,25 @@ public class NeutronSubnet {
             NeutronSubnet_IPAllocationPool pool = i.next();\r
             long lIP = NeutronSubnet_IPAllocationPool.convert(pool.getPoolStart());\r
             long hIP = NeutronSubnet_IPAllocationPool.convert(pool.getPoolEnd());\r
-            if (sIP+1 == lIP)\r
+            if (sIP+1 == lIP) {\r
                 hPool = pool;\r
-            if (sIP-1 == hIP)\r
+            }\r
+            if (sIP-1 == hIP) {\r
                 lPool = pool;\r
+            }\r
         }\r
         //if (lPool == NULL and hPool == NULL) create new pool where low = ip = high\r
-        if (lPool == null && hPool == null)\r
+        if (lPool == null && hPool == null) {\r
             allocationPools.add(new NeutronSubnet_IPAllocationPool(ipAddress,ipAddress));\r
+        }\r
         //if (lPool == NULL and hPool != NULL) change low address of hPool to ipAddr\r
-        if (lPool == null && hPool != null)\r
+        if (lPool == null && hPool != null) {\r
             hPool.setPoolStart(ipAddress);\r
+        }\r
         //if (lPool != NULL and hPool == NULL) change high address of lPool to ipAddr\r
-        if (lPool != null && hPool == null)\r
+        if (lPool != null && hPool == null) {\r
             lPool.setPoolEnd(ipAddress);\r
+        }\r
         //if (lPool != NULL and hPool != NULL) remove lPool and hPool and create new pool\r
         //        where low address = lPool.low address and high address = hPool.high Address\r
         if (lPool != null && hPool != null) {\r
index 08a7149d7afa6c3b533beb472d19ce91587feed2..805f5be29601dcc8026d980b577e69af6758e7f1 100644 (file)
@@ -13,6 +13,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -82,6 +83,9 @@ public class BridgeDomainNorthbound {
      * @param nodeId Node Identifier of the node with the management session.
      * @param bridgeName Name / Identifier for a bridge to be created.
      * @param bridgeConfigs Additional Bridge Configurations.
+     *        It takes in complex structures under the ConfigConstants.CUSTOM key.
+     *        The use-cases are documented under wiki.opendaylight.org project pages:
+     *        https://wiki.opendaylight.org/view/OVSDB_Integration:Mininet_OVSDB_Tutorial
      */
 
    @Path("/bridge/{nodeType}/{nodeId}/{bridgeName}")
@@ -116,6 +120,53 @@ public class BridgeDomainNorthbound {
        throw new ResourceNotFoundException(status.getDescription());
    }
 
+
+   /**
+    * Remove a Bridge.
+    * <pre>
+    *
+    * Example :
+    *
+    * Request :
+    * DELETE
+    * http://localhost:8080/controller/nb/v2/networkconfig/bridgedomain/bridge/STUB/mgmt1/bridge1
+    *
+    *</pre>
+    * @param nodeType Node Type of the node with the management session.
+    * @param nodeId Node Identifier of the node with the management session.
+    * @param bridgeName Name / Identifier for a bridge to be deleted.
+    */
+
+  @Path("/bridge/{nodeType}/{nodeId}/{bridgeName}")
+  @DELETE
+  @StatusCodes( { @ResponseCode(code = 200, condition = "Bridge deleted successfully"),
+      @ResponseCode(code = 404, condition = "Could not delete Bridge"),
+      @ResponseCode(code = 412, condition = "Failed to delete Bridge due to an exception"),
+      @ResponseCode(code = 503, condition = "Bridge Domain Configuration Service not available")} )
+
+  public Response deleteBridge(
+          @PathParam(value = "nodeType") String nodeType,
+          @PathParam(value = "nodeId") String nodeId,
+          @PathParam(value = "bridgeName") String name) {
+
+      IBridgeDomainConfigService configurationService = getConfigurationService();
+      if (configurationService == null) {
+          throw new ServiceUnavailableException("IBridgeDomainConfigService not available.");
+      }
+
+      Node node = Node.fromString(nodeType, nodeId);
+      Status status = null;
+      try {
+          status = configurationService.deleteBridgeDomain(node, name);
+          if (status.getCode().equals(StatusCode.SUCCESS)) {
+              return Response.status(Response.Status.OK).build();
+          }
+      } catch (Throwable t) {
+          return Response.status(Response.Status.PRECONDITION_FAILED).build();
+      }
+      throw new ResourceNotFoundException(status.getDescription());
+  }
+
    /**
     * Add a Port to a Bridge
     * <pre>
@@ -131,6 +182,9 @@ public class BridgeDomainNorthbound {
     * @param bridgeName Name / Identifier of the bridge to which a Port is being added.
     * @param portName Name / Identifier of a Port that is being added to a bridge.
     * @param portConfigs Additional Port Configurations.
+    *        It takes in complex structures under the ConfigConstants.CUSTOM key.
+    *        The use-cases are documented under wiki.opendaylight.org project pages :
+    *        https://wiki.opendaylight.org/view/OVSDB_Integration:Mininet_OVSDB_Tutorial
     */
 
    @Path("/port/{nodeType}/{nodeId}/{bridgeName}/{portName}")
@@ -167,6 +221,55 @@ public class BridgeDomainNorthbound {
        throw new ResourceNotFoundException(status.getDescription());
    }
 
+   /**
+    * Remove a Port from a Bridge
+    * <pre>
+    *
+    * Example :
+    *
+    * Request :
+    * DELETE
+    * http://localhost:8080/controller/nb/v2/networkconfig/bridgedomain/port/STUB/mgmt1/bridge1/port1
+    *
+    *</pre>
+    * @param nodeType Node Type of the node with the management session.
+    * @param nodeId Node Identifier of the node with the management session.
+    * @param bridgeName Name / Identifier of the bridge to which a Port is being added.
+    * @param portName Name / Identifier of a Port that is being deleted from a bridge.
+    */
+
+   @Path("/port/{nodeType}/{nodeId}/{bridgeName}/{portName}")
+   @DELETE
+   @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+   @StatusCodes( { @ResponseCode(code = 200, condition = "Port deleted successfully"),
+       @ResponseCode(code = 404, condition = "Could not delete Port to the Bridge"),
+       @ResponseCode(code = 412, condition = "Failed to delete Port due to an exception"),
+       @ResponseCode(code = 503, condition = "Bridge Domain Configuration Service not available")} )
+
+   public Response deletePort(
+           @PathParam(value = "nodeType") String nodeType,
+           @PathParam(value = "nodeId") String nodeId,
+           @PathParam(value = "bridgeName") String bridge,
+           @PathParam(value = "portName") String port) {
+
+       IBridgeDomainConfigService configurationService = getConfigurationService();
+       if (configurationService == null) {
+           throw new ServiceUnavailableException("IBridgeDomainConfigService not available.");
+       }
+
+       Node node = Node.fromString(nodeType, nodeId);
+       Status status = null;
+       try {
+           status = configurationService.deletePort(node, bridge, port);
+           if (status.getCode().equals(StatusCode.SUCCESS)) {
+               return Response.status(Response.Status.OK).build();
+           }
+       } catch (Throwable t) {
+           return Response.status(Response.Status.PRECONDITION_FAILED).build();
+       }
+       throw new ResourceNotFoundException(status.getDescription());
+   }
+
    private Map<ConfigConstants, Object> buildConfig(Map<String, Object> rawConfigs) {
        if (rawConfigs == null) return null;
        Map<ConfigConstants, Object> configs = new HashMap<ConfigConstants, Object>();
index bb871a1a77a0f725f1f7510f7095b7ec65131474..455fbdd1c718d25be6a0beeb58cce12e51e7f658 100644 (file)
@@ -26,7 +26,7 @@
           </init-param>
           <init-param>
             <param-name>cors.allowed.methods</param-name>
-            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+            <param-value>GET,POST,DELETE,HEAD,OPTIONS,PUT</param-value>
           </init-param>
           <init-param>
             <param-name>cors.allowed.headers</param-name>
index d2d7a5a671c62325ce1a0d2f8966901867357878..699aee9fc3a7d3503e7b1a6d464926da1aefd24f 100644 (file)
@@ -33,6 +33,7 @@ import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;\r
 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;\r
 import org.opendaylight.controller.northbound.commons.RestMessages;\r
+import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;\r
 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;\r
 import org.opendaylight.controller.sal.utils.ServiceHelper;\r
 \r
@@ -107,10 +108,11 @@ public class NeutronSubnetsNorthbound {
                     (queryGatewayIP == null || queryGatewayIP.equals(oSS.getGatewayIP())) &&\r
                     (queryEnableDHCP == null || queryEnableDHCP.equals(oSS.getEnableDHCP())) &&\r
                     (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {\r
-                if (fields.size() > 0)\r
+                if (fields.size() > 0) {\r
                     ans.add(extractFields(oSS,fields));\r
-                else\r
+                } else {\r
                     ans.add(oSS);\r
+                }\r
             }\r
         }\r
         //TODO: apply pagination to results\r
@@ -139,15 +141,17 @@ public class NeutronSubnetsNorthbound {
             throw new ServiceUnavailableException("Subnet CRUD Interface "\r
                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
         }\r
-        if (!subnetInterface.subnetExists(subnetUUID))\r
+        if (!subnetInterface.subnetExists(subnetUUID)) {\r
             return Response.status(404).build();\r
+        }\r
         if (fields.size() > 0) {\r
             NeutronSubnet ans = subnetInterface.getSubnet(subnetUUID);\r
             return Response.status(200).entity(\r
                     new NeutronSubnetRequest(extractFields(ans, fields))).build();\r
-        } else\r
+        } else {\r
             return Response.status(200).entity(\r
                     new NeutronSubnetRequest(subnetInterface.getSubnet(subnetUUID))).build();\r
+        }\r
     }\r
 \r
     /**\r
@@ -185,22 +189,29 @@ public class NeutronSubnetsNorthbound {
              *  and that the gateway IP doesn't overlap with the allocation pools\r
              *  *then* add the subnet to the cache\r
              */\r
-            if (subnetInterface.subnetExists(singleton.getID()))\r
+            if (subnetInterface.subnetExists(singleton.getID())) {\r
                 return Response.status(400).build();\r
-            if (!networkInterface.networkExists(singleton.getNetworkUUID()))\r
+            }\r
+            if (!networkInterface.networkExists(singleton.getNetworkUUID())) {\r
                 return Response.status(404).build();\r
-            if (!singleton.isValidCIDR())\r
+            }\r
+            if (!singleton.isValidCIDR()) {\r
                 return Response.status(400).build();\r
-            singleton.initDefaults();\r
-            if (singleton.gatewayIP_Pool_overlap())\r
+            }\r
+            if (!singleton.initDefaults()) {\r
+                throw new InternalServerErrorException("subnet object could not be initialized properly");\r
+            }\r
+            if (singleton.gatewayIP_Pool_overlap()) {\r
                 return Response.status(409).build();\r
+            }\r
             Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);\r
             if (instances != null) {\r
                 for (Object instance : instances) {\r
                     INeutronSubnetAware service = (INeutronSubnetAware) instance;\r
                     int status = service.canCreateSubnet(singleton);\r
-                    if (status < 200 || status > 299)\r
+                    if (status < 200 || status > 299) {\r
                         return Response.status(status).build();\r
+                    }\r
                 }\r
             }\r
             subnetInterface.addSubnet(singleton);\r
@@ -225,24 +236,32 @@ public class NeutronSubnetsNorthbound {
                  *  and that the bulk request doesn't already contain a subnet with this id\r
                  */\r
 \r
-                test.initDefaults();\r
-                if (subnetInterface.subnetExists(test.getID()))\r
+                if (!test.initDefaults()) {\r
+                    throw new InternalServerErrorException("subnet object could not be initialized properly");\r
+                }\r
+                if (subnetInterface.subnetExists(test.getID())) {\r
                     return Response.status(400).build();\r
-                if (testMap.containsKey(test.getID()))\r
+                }\r
+                if (testMap.containsKey(test.getID())) {\r
                     return Response.status(400).build();\r
+                }\r
                 testMap.put(test.getID(), test);\r
-                if (!networkInterface.networkExists(test.getNetworkUUID()))\r
+                if (!networkInterface.networkExists(test.getNetworkUUID())) {\r
                     return Response.status(404).build();\r
-                if (!test.isValidCIDR())\r
+                }\r
+                if (!test.isValidCIDR()) {\r
                     return Response.status(400).build();\r
-                if (test.gatewayIP_Pool_overlap())\r
+                }\r
+                if (test.gatewayIP_Pool_overlap()) {\r
                     return Response.status(409).build();\r
+                }\r
                 if (instances != null) {\r
                     for (Object instance : instances) {\r
                         INeutronSubnetAware service = (INeutronSubnetAware) instance;\r
                         int status = service.canCreateSubnet(test);\r
-                        if (status < 200 || status > 299)\r
+                        if (status < 200 || status > 299) {\r
                             return Response.status(status).build();\r
+                        }\r
                     }\r
                 }\r
             }\r
@@ -292,10 +311,12 @@ public class NeutronSubnetsNorthbound {
         /*\r
          * verify the subnet exists and there is only one delta provided\r
          */\r
-        if (!subnetInterface.subnetExists(subnetUUID))\r
+        if (!subnetInterface.subnetExists(subnetUUID)) {\r
             return Response.status(404).build();\r
-        if (!input.isSingleton())\r
+        }\r
+        if (!input.isSingleton()) {\r
             return Response.status(400).build();\r
+        }\r
         NeutronSubnet delta = input.getSingleton();\r
         NeutronSubnet original = subnetInterface.getSubnet(subnetUUID);\r
 \r
@@ -304,16 +325,18 @@ public class NeutronSubnetsNorthbound {
          */\r
         if (delta.getID() != null || delta.getTenantID() != null ||\r
                 delta.getIpVersion() != null || delta.getCidr() != null ||\r
-                delta.getAllocationPools() != null)\r
+                delta.getAllocationPools() != null) {\r
             return Response.status(400).build();\r
+        }\r
 \r
         Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);\r
         if (instances != null) {\r
             for (Object instance : instances) {\r
                 INeutronSubnetAware service = (INeutronSubnetAware) instance;\r
                 int status = service.canUpdateSubnet(delta, original);\r
-                if (status < 200 || status > 299)\r
+                if (status < 200 || status > 299) {\r
                     return Response.status(status).build();\r
+                }\r
             }\r
         }\r
 \r
@@ -354,18 +377,21 @@ public class NeutronSubnetsNorthbound {
         /*\r
          * verify the subnet exists and it isn't currently in use\r
          */\r
-        if (!subnetInterface.subnetExists(subnetUUID))\r
+        if (!subnetInterface.subnetExists(subnetUUID)) {\r
             return Response.status(404).build();\r
-        if (subnetInterface.subnetInUse(subnetUUID))\r
+        }\r
+        if (subnetInterface.subnetInUse(subnetUUID)) {\r
             return Response.status(409).build();\r
+        }\r
         NeutronSubnet singleton = subnetInterface.getSubnet(subnetUUID);\r
         Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);\r
         if (instances != null) {\r
             for (Object instance : instances) {\r
                 INeutronSubnetAware service = (INeutronSubnetAware) instance;\r
                 int status = service.canDeleteSubnet(singleton);\r
-                if (status < 200 || status > 299)\r
+                if (status < 200 || status > 299) {\r
                     return Response.status(status).build();\r
+                }\r
             }\r
         }\r
 \r
index 91261f6499fe6092595117d9f90e1eb967f4003a..3d33edcbf2272c308cdb2236c574aaa42cd95ed9 100644 (file)
@@ -751,9 +751,15 @@ public class Devices implements IDaylightWeb {
 
         Set<Node> nodes = connectionManager.getLocalNodes();
         List<NodeJsonBean> result = new LinkedList<NodeJsonBean>();
+        if (nodes == null) {
+            return result;
+        }
         for (Node node : nodes) {
             Description descriptionProperty = (Description) switchManager.getNodeProp(node, "description");
-            String description = descriptionProperty.getValue();
+            String description = node.toString();
+            if (descriptionProperty != null) {
+                description = descriptionProperty.getValue();
+            }
             NodeJsonBean nodeBean = new NodeJsonBean();
             nodeBean.setNodeId(node.getNodeIDString());
             nodeBean.setNodeType(node.getType());
@@ -860,4 +866,4 @@ public class Devices implements IDaylightWeb {
         return "forward:" + "/";
     }
 
-}
\ No newline at end of file
+}