Merge "Change fix for default module's dependency resolver creation into two steps."
authorEd Warnicke <eaw@cisco.com>
Fri, 8 Nov 2013 10:41:53 +0000 (10:41 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 8 Nov 2013 10:41:53 +0000 (10:41 +0000)
92 files changed:
opendaylight/commons/checkstyle/src/main/resources/controller/checkstyle.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FixedThreadPoolWrapper.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/NamingThreadPoolFactory.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/ScheduledThreadPoolWrapper.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModule.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModuleFactory.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModule.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModuleFactory.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModule.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModuleFactory.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModule.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModuleFactory.java [new file with mode: 0644]
opendaylight/config/threadpool-config-impl/src/main/yang/threadpool-impl.yang
opendaylight/distribution/opendaylight/pom.xml
opendaylight/forwardingrulesmanager_mdsal/openflow/pom.xml
opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/FRMConsumerImpl.java
opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/FRMUtil.java [new file with mode: 0644]
opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/FlowConsumerImpl.java
opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/GroupConsumerImpl.java
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.xtend [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowServiceAdapter.java
opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/TestFromSalConversionsUtils.java
opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/TestToSalConversionsUtils.java
opendaylight/md-sal/model/model-flow-base/src/main/yang/group-types.yang
opendaylight/md-sal/model/model-flow-base/src/main/yang/meter-types.yang
opendaylight/md-sal/model/model-flow-base/src/main/yang/port-types.yang
opendaylight/md-sal/model/model-flow-management/src/main/yang/meter-config.yang
opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-capable-transaction.yang [new file with mode: 0644]
opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang
opendaylight/md-sal/model/model-flow-service/src/main/yang/meter-service.yang
opendaylight/md-sal/model/model-flow-service/src/main/yang/port-service.yang [new file with mode: 0644]
opendaylight/md-sal/model/model-flow-service/src/main/yang/table-service.yang
opendaylight/md-sal/sal-binding-api/pom.xml
opendaylight/md-sal/sal-binding-broker/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataBrokerImpl.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataProviderContext.xtend [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataTransactionImpl.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.xtend [new file with mode: 0644]
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/DataCommitHandler.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-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/pom.xml
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractDataModification.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/AbstractDataReadRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcConsumptionRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/data/DataProviderService.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionInstance.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/pom.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.xtend
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/NotificationRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RoutedRpcProcessor.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RpcRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/pom.xml
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/pom.xml
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/NodeJsonBean.java
opendaylight/web/devices/src/main/resources/js/page.js

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>
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FixedThreadPoolWrapper.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FixedThreadPoolWrapper.java
new file mode 100644 (file)
index 0000000..ca03443
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.config.threadpool.util;
+
+import java.io.Closeable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+
+/**
+ * Implementation of {@link ThreadPool} using fixed number of threads wraps
+ * {@link ExecutorService}.
+ */
+public class FixedThreadPoolWrapper implements ThreadPool, Closeable {
+
+    private final ThreadPoolExecutor executor;
+
+    public FixedThreadPoolWrapper(int threadCount, ThreadFactory factory) {
+        this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadCount, factory);
+        executor.prestartAllCoreThreads();
+    }
+
+    @Override
+    public ExecutorService getExecutor() {
+        return Executors.unconfigurableExecutorService(executor);
+    }
+
+    @Override
+    public void close() {
+        executor.shutdown();
+    }
+
+    @Override
+    public int getMaxThreadCount() {
+        return executor.getMaximumPoolSize();
+    }
+
+    public void setMaxThreadCount(int maxThreadCount) {
+        executor.setCorePoolSize(maxThreadCount);
+        executor.setMaximumPoolSize(maxThreadCount);
+    }
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java
new file mode 100644 (file)
index 0000000..3dfa6e2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.config.threadpool.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+
+/**
+ * Implementation of {@link ThreadPool} using flexible number of threads wraps
+ * {@link ExecutorService}.
+ */
+public class FlexibleThreadPoolWrapper implements ThreadPool, Closeable {
+    private final ThreadPoolExecutor executor;
+
+    public FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit,
+            ThreadFactory threadFactory) {
+
+        executor = new ThreadPoolExecutor(minThreadCount, maxThreadCount, keepAlive, timeUnit,
+                new SynchronousQueue<Runnable>(), threadFactory);
+        executor.prestartAllCoreThreads();
+    }
+
+    @Override
+    public ExecutorService getExecutor() {
+        return Executors.unconfigurableExecutorService(executor);
+    }
+
+    public int getMinThreadCount() {
+        return executor.getCorePoolSize();
+    }
+
+    public void setMinThreadCount(int minThreadCount) {
+        executor.setCorePoolSize(minThreadCount);
+    }
+
+    @Override
+    public int getMaxThreadCount() {
+        return executor.getMaximumPoolSize();
+    }
+
+    public void setMaxThreadCount(int maxThreadCount) {
+        executor.setMaximumPoolSize(maxThreadCount);
+    }
+
+    public long getKeepAliveMillis() {
+        return executor.getKeepAliveTime(TimeUnit.MILLISECONDS);
+    }
+
+    public void setKeepAliveMillis(long keepAliveMillis) {
+        executor.setKeepAliveTime(keepAliveMillis, TimeUnit.MILLISECONDS);
+    }
+
+    public void setThreadFactory(ThreadFactory threadFactory) {
+        executor.setThreadFactory(threadFactory);
+    }
+
+    public void prestartAllCoreThreads() {
+        executor.prestartAllCoreThreads();
+    }
+
+    @Override
+    public void close() throws IOException {
+        executor.shutdown();
+    }
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/NamingThreadPoolFactory.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/NamingThreadPoolFactory.java
new file mode 100644 (file)
index 0000000..2e27d6c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.config.threadpool.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implementation of {@link ThreadFactory}.
+ */
+@ThreadSafe
+public class NamingThreadPoolFactory implements ThreadFactory, Closeable {
+
+    private final ThreadGroup group;
+    private final String namePrefix;
+    private final AtomicLong threadName = new AtomicLong();
+
+    public NamingThreadPoolFactory(String namePrefix) {
+        Preconditions.checkNotNull(namePrefix);
+        this.group = new ThreadGroup(namePrefix);
+        this.namePrefix = namePrefix;
+    }
+
+    @Override
+    public Thread newThread(Runnable r) {
+        return new Thread(group, r, String.format("%s-%d", group.getName(), threadName.incrementAndGet()));
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+
+    public String getNamePrefix() {
+        return namePrefix;
+    }
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/ScheduledThreadPoolWrapper.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/ScheduledThreadPoolWrapper.java
new file mode 100644 (file)
index 0000000..ee3399e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.config.threadpool.util;
+
+import java.io.Closeable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+
+/**
+ * Implementation of {@link ScheduledThreadPool} wraps
+ * {@link ScheduledExecutorService}.
+ */
+public class ScheduledThreadPoolWrapper implements ScheduledThreadPool, Closeable {
+
+    private final ScheduledThreadPoolExecutor executor;
+    private final int threadCount;
+
+    public ScheduledThreadPoolWrapper(int threadCount, ThreadFactory factory) {
+        this.threadCount = threadCount;
+        this.executor = new ScheduledThreadPoolExecutor(threadCount, factory);
+        executor.prestartAllCoreThreads();
+    }
+
+    @Override
+    public ScheduledExecutorService getExecutor() {
+        return Executors.unconfigurableScheduledExecutorService(executor);
+    }
+
+    @Override
+    public void close() {
+        executor.shutdown();
+    }
+
+    @Override
+    public int getMaxThreadCount() {
+        return threadCount;
+    }
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModule.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModule.java
new file mode 100644 (file)
index 0000000..a0a9add
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-fixed
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.threadpool.util.FixedThreadPoolWrapper;
+
+/**
+*
+*/
+public final class FixedThreadPoolModule extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractFixedThreadPoolModule {
+
+    public FixedThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public FixedThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            FixedThreadPoolModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+
+        JmxAttributeValidationException.checkNotNull(getMaxThreadCount(), maxThreadCountJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getMaxThreadCount() > 0, "must be greater than zero",
+                maxThreadCountJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new FixedThreadPoolWrapper(getMaxThreadCount(), getThreadFactoryDependency());
+    }
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModuleFactory.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FixedThreadPoolModuleFactory.java
new file mode 100644 (file)
index 0000000..2803448
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-fixed
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+/**
+*
+*/
+public class FixedThreadPoolModuleFactory extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractFixedThreadPoolModuleFactory {
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModule.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModule.java
new file mode 100644 (file)
index 0000000..47b4eec
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-flexible
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.threadpool.util.FlexibleThreadPoolWrapper;
+
+/**
+*
+*/
+public final class FlexibleThreadPoolModule extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractFlexibleThreadPoolModule {
+
+    public FlexibleThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public FlexibleThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            FlexibleThreadPoolModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        JmxAttributeValidationException.checkNotNull(getKeepAliveMillis(), keepAliveMillisJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getKeepAliveMillis() > 0, "must be greater than zero",
+                keepAliveMillisJmxAttribute);
+
+        JmxAttributeValidationException.checkNotNull(getMinThreadCount(), minThreadCountJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getMinThreadCount() > 0, "must be greater than zero",
+                minThreadCountJmxAttribute);
+
+        JmxAttributeValidationException.checkNotNull(getMaxThreadCount(), maxThreadCountJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getMaxThreadCount() > 0, "must be greater than zero",
+                maxThreadCountJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new FlexibleThreadPoolWrapper(getMinThreadCount(), getMaxThreadCount(), getKeepAliveMillis(),
+                TimeUnit.MILLISECONDS, getThreadFactoryDependency());
+    }
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModuleFactory.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/FlexibleThreadPoolModuleFactory.java
new file mode 100644 (file)
index 0000000..ef6ebd2
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-flexible
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+/**
+*
+*/
+public class FlexibleThreadPoolModuleFactory extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractFlexibleThreadPoolModuleFactory {
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModule.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModule.java
new file mode 100644 (file)
index 0000000..a761727
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadfactory-naming
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory;
+
+/**
+*
+*/
+public final class NamingThreadFactoryModule extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractNamingThreadFactoryModule {
+
+    public NamingThreadFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public NamingThreadFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            NamingThreadFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        JmxAttributeValidationException.checkNotNull(getNamePrefix(), namePrefixJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new NamingThreadPoolFactory(getNamePrefix());
+    }
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModuleFactory.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/NamingThreadFactoryModuleFactory.java
new file mode 100644 (file)
index 0000000..5e70afb
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadfactory-naming
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+/**
+*
+*/
+public class NamingThreadFactoryModuleFactory extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractNamingThreadFactoryModuleFactory {
+
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModule.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModule.java
new file mode 100644 (file)
index 0000000..42739b4
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-scheduled
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper;
+
+/**
+*
+*/
+public final class ScheduledThreadPoolModule extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractScheduledThreadPoolModule {
+
+    public ScheduledThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public ScheduledThreadPoolModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            ScheduledThreadPoolModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        JmxAttributeValidationException.checkNotNull(getMaxThreadCount(), maxThreadCountJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getMaxThreadCount() > 0, "must be greater than zero",
+                maxThreadCountJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new ScheduledThreadPoolWrapper(getMaxThreadCount(), getThreadFactoryDependency());
+    }
+}
diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModuleFactory.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/ScheduledThreadPoolModuleFactory.java
new file mode 100644 (file)
index 0000000..489af82
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: threadpool-impl  yang module local name: threadpool-scheduled
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 06 16:19:33 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.threadpool.impl;
+
+/**
+*
+*/
+public class ScheduledThreadPoolModuleFactory extends
+        org.opendaylight.controller.config.yang.threadpool.impl.AbstractScheduledThreadPoolModuleFactory {
+
+}
index 469556414492672784e67387a93ac577eb8c5076..a2366f285a0c0b8682b1093f18fb5ee184c9cde3 100644 (file)
@@ -83,8 +83,6 @@ module threadpool-impl {
         }
     }
 
-
-
        rpc get-dead-events-count {
                config:java-name-prefix countDeadEvents;
                input {
@@ -100,5 +98,98 @@ module threadpool-impl {
                        }
                }
        }
+       
+       identity threadfactory-naming {
+        base config:module-type;
+               config:provided-service th:threadfactory;
+        config:java-name-prefix NamingThreadFactory;
+    }
+
+       augment "/config:modules/config:module/config:configuration" {
+               case threadfactory-naming {
+                       when "/config:modules/config:module/config:type = 'threadfactory-naming'";
+                       leaf name-prefix {
+                               type string;
+                       }
+        }
+    }
+
+    identity threadpool-fixed {
+       base config:module-type;
+       config:provided-service th:threadpool;
+       config:java-name-prefix FixedThreadPool;
+    }
+
+       augment "/config:modules/config:module/config:configuration" {
+               case threadpool-fixed {
+                       when "/config:modules/config:module/config:type = 'threadpool-fixed'";
+                       leaf max-thread-count {
+                               type uint16;
+                       }
+
+                       container threadFactory {
+                               uses config:service-ref {
+                                       refine type {
+                                               //mandatory true;
+                                               config:required-identity th:threadfactory;
+                                       }
+                               }
+                       }
+        }
+       }
+
+       identity threadpool-flexible {
+               base config:module-type;
+               config:provided-service th:threadpool;
+               config:java-name-prefix FlexibleThreadPool;
+       }
+
+       augment "/config:modules/config:module/config:configuration" {
+               case threadpool-flexible {
+                       when "/config:modules/config:module/config:type = 'threadpool-flexible'";
+                       leaf max-thread-count {
+                               type uint16;
+                       }
+                       leaf minThreadCount {
+                               type uint16;
+                       }
+                       leaf keepAliveMillis {
+                               type uint32;
+                       }
+
+                       container threadFactory {
+                               uses config:service-ref {
+                                       refine type {
+                                          // mandatory true;
+                                               config:required-identity th:threadfactory;
+                                       }
+                               }
+                       }
+        }
+       }
+
+    identity threadpool-scheduled {
+               base config:module-type;
+               config:provided-service th:scheduled-threadpool;
+               config:java-name-prefix ScheduledThreadPool;
+       }
+
+       augment "/config:modules/config:module/config:configuration" {
+               case threadpool-scheduled {
+                       when "/config:modules/config:module/config:type = 'threadpool-scheduled'";
+                       leaf max-thread-count {
+                               type uint16;
+                       }
+
+                       container threadFactory {
+                               uses config:service-ref {
+                                       refine type {
+                                        //   mandatory true;
+                                               config:required-identity th:threadfactory;
+                                       }
+                               }
+                       }
+               }
+       }
 }
 
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 9b96f86c92497c70ec8c5b03c0d01aae70538224..29de77270bb6d63c5f791f0ad70cce2eda6d9250 100644 (file)
             <Import-Package>
               org.opendaylight.controller.sal.binding.api,
               org.opendaylight.controller.sal.binding.api.data,
-              org.opendaylight.controller.md.sal.common.api.data,              
-              org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.flow,
+              org.opendaylight.controller.md.sal.common.api.data, 
+              org.opendaylight.controller.sal.utils, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.config.rev131024.groups, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.bucket,
               org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819,
+              org.opendaylight.controller.clustering.services, org.opendaylight.controller.sal.core, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction,
+              org.opendaylight.controller.switchmanager, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list, 
+              org.opendaylight.yang.gen.v1.urn.opendaylight.group.config.rev131024,
               org.opendaylight.yangtools.concepts,              
               org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819,
               org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819,
index ae488b689f4788c95d017a07c53cb0ddecb9f0fd..e48e03867c89025fc62babeab7548a8947a27712 100644 (file)
 package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
 
 
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
 import org.opendaylight.controller.sal.binding.api.NotificationService;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
-public class FRMConsumerImpl extends AbstractBindingAwareProvider {
+public class FRMConsumerImpl extends AbstractBindingAwareProvider implements CommandProvider{
        protected static final Logger logger = LoggerFactory.getLogger(FRMConsumerImpl.class);
        private static ProviderContext p_session;
     private static DataBrokerService dataBrokerService;         
@@ -28,11 +35,21 @@ public class FRMConsumerImpl extends AbstractBindingAwareProvider {
        private GroupConsumerImpl groupImplRef;
        private static DataProviderService dataProviderService;  
 
+       private static IClusterContainerServices clusterContainerService = null;
+       private static ISwitchManager switchManager;
+       private static IContainer container;
+       
        @Override
     public void onSessionInitiated(ProviderContext session) {
        
         FRMConsumerImpl.p_session = session;
         
+        if (!getDependentModule()) {
+            logger.error("Unable to fetch handlers for dependent modules");
+            System.out.println("Unable to fetch handlers for dependent modules");
+            return;
+        }
+        
         if (null != session) {
                notificationService = session.getSALService(NotificationService.class);
                
@@ -44,7 +61,8 @@ public class FRMConsumerImpl extends AbstractBindingAwareProvider {
                                
                                if (null != dataProviderService) {
                                        flowImplRef = new FlowConsumerImpl();
-                                       groupImplRef = new GroupConsumerImpl();
+                       //              groupImplRef = new GroupConsumerImpl();
+                                       registerWithOSGIConsole();
                                }
                                else {
                                        logger.error("Data Provider Service is down or NULL. " +
@@ -68,18 +86,85 @@ public class FRMConsumerImpl extends AbstractBindingAwareProvider {
                System.out.println("Consumer session is NULL. Please check if provider is registered");
         }
   
+    }
+       
+       public static IClusterContainerServices getClusterContainerService() {
+        return clusterContainerService;
+    }
+
+    public static void setClusterContainerService(
+            IClusterContainerServices clusterContainerService) {
+        FRMConsumerImpl.clusterContainerService = clusterContainerService;
+    }
+
+    public static ISwitchManager getSwitchManager() {
+        return switchManager;
+    }
+
+    public static void setSwitchManager(ISwitchManager switchManager) {
+        FRMConsumerImpl.switchManager = switchManager;
+    }
+
+    public static IContainer getContainer() {
+        return container;
     }
 
-       public static DataProviderService getDataProviderService() {
+    public static void setContainer(IContainer container) {
+        FRMConsumerImpl.container = container;
+    }
+
+    private void registerWithOSGIConsole() {
+        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+        bundleContext.registerService(CommandProvider.class.getName(), this, null);
+    }
+       
+       private boolean getDependentModule() {
+           do {
+        clusterContainerService = (IClusterContainerServices) ServiceHelper.getGlobalInstance(IClusterContainerServices.class, this);
+        try {
+            Thread.sleep(4);
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+           } while(clusterContainerService == null);
+           
+           do {
+               
+           
+        container = (IContainer) ServiceHelper.getGlobalInstance(IContainer.class, this);
+        try {
+            Thread.sleep(5);
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+           } while (container == null);
+           
+           do {
+               switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, container.getName(), this);
+               try {
+                   Thread.sleep(5);
+               } catch (InterruptedException e) {
+                   // TODO Auto-generated catch block
+                   e.printStackTrace();
+               }
+           } while(null == switchManager);
+        return true;
+       }
+
+       
+
+    public static DataProviderService getDataProviderService() {
                return dataProviderService;
        }
 
        public FlowConsumerImpl getFlowImplRef() {
-               return flowImplRef;
+           return flowImplRef;
        }
 
        public GroupConsumerImpl getGroupImplRef() {
-                       return groupImplRef;
+           return groupImplRef;
        }
         
        public static ProviderContext getProviderSession() {
@@ -93,6 +178,15 @@ public class FRMConsumerImpl extends AbstractBindingAwareProvider {
        public static DataBrokerService getDataBrokerService() {
                return dataBrokerService;
        }
+       
+       /*
+     * OSGI COMMANDS
+     */
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        return help.toString();
+    }
 
 }
        
diff --git a/opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/FRMUtil.java b/opendaylight/forwardingrulesmanager_mdsal/openflow/src/main/java/org/opendaylight/controller/forwardingrulesmanager_mdsal/consumer/impl/FRMUtil.java
new file mode 100644 (file)
index 0000000..df34d19
--- /dev/null
@@ -0,0 +1,33 @@
+package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.bucket.Actions;
+
+public class FRMUtil {
+    private static final String NAMEREGEX = "^[a-zA-Z0-9]+$";
+    public enum operation {ADD, DELETE, UPDATE, GET};
+    
+    
+    public static boolean isNameValid(String name) {
+    
+        //  Name validation 
+        if (name == null || name.trim().isEmpty() || !name.matches(NAMEREGEX)) {
+            return false;
+        }
+        return true;
+        
+    }
+    
+    public static boolean areActionsValid(Actions actions) {
+     //   List<Action> actionList;
+       // Action actionRef;
+      //  if (null != actions && null != actions.getAction()) {
+       //     actionList = actions.getAction();
+            
+
+               
+       // }
+        
+        return true;
+    }
+}
index a6a3c71325ed7e644918527ef600fbe739f7faee..59c7e043de823a83a4f6b0318c2d25650bd4b384 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
@@ -27,6 +28,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.Flow
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 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;
@@ -61,6 +63,7 @@ public class FlowConsumerImpl {
                }
                
                listener = new FlowDataListener();
+               
                if (null == FRMConsumerImpl.getDataBrokerService().registerDataChangeListener(path, listener)) {
                        logger.error("Failed to listen on flow data modifcation events");
                System.out.println("Consumer SAL Service is down or NULL.");
@@ -75,7 +78,7 @@ public class FlowConsumerImpl {
                System.out.println("Consumer SAL Service is down or NULL.");
                return;
                }
-               addFlowTest();
+               //addFlowTest();
                System.out.println("-------------------------------------------------------------------");
                allocateCaches();
                commitHandler = new FlowDataCommitHandler();
@@ -120,11 +123,19 @@ public class FlowConsumerImpl {
     private void addFlow(InstanceIdentifier<?> path, Flow dataObject) {
 
         AddFlowInputBuilder input = new AddFlowInputBuilder();
+        List<Instruction> inst = (dataObject).getInstructions().getInstruction();
         input.setNode((dataObject).getNode());
         input.setPriority((dataObject).getPriority());
         input.setMatch((dataObject).getMatch());
         input.setCookie((dataObject).getCookie());
-        input.setAction((dataObject).getAction());
+        input.setInstructions((dataObject).getInstructions());
+        dataObject.getMatch().getLayer3Match()
+        for (int i=0;i<inst.size();i++) {
+            System.out.println("i = "+ i + inst.get(i).getInstruction().toString());
+            System.out.println("i = "+ i + inst.get(i).toString());
+        }
+        
+        System.out.println("Instruction list" + (dataObject).getInstructions().getInstruction().toString());
 
         // We send flow to the sounthbound plugin
         flowService.addFlow(input.build());
@@ -132,9 +143,11 @@ public class FlowConsumerImpl {
     
     private void commitToPlugin(internalTransaction transaction) {
         for(Entry<InstanceIdentifier<?>, Flow> entry :transaction.additions.entrySet()) {
+            System.out.println("Coming add cc in FlowDatacommitHandler");
             addFlow(entry.getKey(),entry.getValue());
         }
-        for(@SuppressWarnings("unused") Entry<InstanceIdentifier<?>, Flow> entry :transaction.additions.entrySet()) {
+        for(@SuppressWarnings("unused") Entry<InstanceIdentifier<?>, Flow> entry :transaction.updates.entrySet()) {
+            System.out.println("Coming update cc in FlowDatacommitHandler");
            // updateFlow(entry.getKey(),entry.getValue());
         }
         
@@ -196,9 +209,11 @@ public class FlowConsumerImpl {
             Flow original = originalSwView.get(key);
             if (original != null) {
                 // It is update for us
+                System.out.println("Coming update  in FlowDatacommitHandler");
                 updates.put(key, flow);
             } else {
                 // It is addition for us
+                System.out.println("Coming add in FlowDatacommitHandler");
                 additions.put(key, flow);
             }
         }
@@ -273,7 +288,7 @@ public class FlowConsumerImpl {
                        for (DataObject dataObject : additions) {
                            if (dataObject instanceof NodeFlow) {
                                NodeRef nodeOne = createNodeRef("foo:node:1");
-                                       // validating the dataObject here
+                                       // validating the dataObject here                               
                                    AddFlowInputBuilder input = new AddFlowInputBuilder();
                                    input.setNode(((NodeFlow) dataObject).getNode());
                                    input.setNode(nodeOne);
@@ -300,18 +315,6 @@ public class FlowConsumerImpl {
         return new NodeRef(path);
     }
            
-         /*  private void loadFlowData() {
        
-                   DataModification modification = (DataModification) dataservice.beginTransaction();
-                   String id = "abc";
-                   FlowKey key = new FlowKey(id, new NodeRef());
-                   InstanceIdentifier<?> path1;
-                   FlowBuilder flow = new FlowBuilder();
-                   flow.setKey(key);
-                   path1 = InstanceIdentifier.builder().node(Flows.class).node(Flow.class, key).toInstance();
-                   DataObject cls = (DataObject) modification.readConfigurationData(path);
-                   modification.putConfigurationData(path, flow.build());
-                   modification.commit();
-               }*/
 
 }
index cc42e21f2a1fc8e0199455f9d7cc65d6b3521183..acc0dc69cd4d059b074fc7691bc1b73d75130170 100644 (file)
@@ -1,7 +1,478 @@
 package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
 
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+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.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.config.rev131024.Groups;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.config.rev131024.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.config.rev131024.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes.GroupType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("unused")
 public class GroupConsumerImpl {
-       public GroupConsumerImpl() {
-               
+    
+    protected static final Logger logger = LoggerFactory.getLogger(GroupConsumerImpl.class);
+    private GroupEventListener groupEventListener = new GroupEventListener();
+    private Registration<NotificationListener> groupListener;
+    private SalGroupService groupService;    
+    private GroupDataCommitHandler commitHandler;
+    
+    private ConcurrentMap<GroupKey, Group> originalSwGroupView;
+    private ConcurrentMap<GroupKey, Group> installedSwGroupView;
+    
+    private ConcurrentMap<Node, List<Group>> nodeGroups;
+    private ConcurrentMap<GroupKey, Group> inactiveGroups;
+    
+    private IClusterContainerServices clusterGroupContainerService = null;
+    private ISwitchManager switchGroupManager;
+    private IContainer container;
+    
+    public GroupConsumerImpl() {
+           InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder().node(Groups.class).toInstance();
+        groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
+        
+        clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService();
+        switchGroupManager = FRMConsumerImpl.getSwitchManager();
+        container = FRMConsumerImpl.getContainer();
+        
+        if (!(cacheStartup())) {
+            logger.error("Unanle to allocate/retrieve group cache");
+            System.out.println("Unable to allocate/retrieve group cache");
+        }
+        
+        if (null == groupService) {
+            logger.error("Consumer SAL Group Service is down or NULL. FRM may not function as intended");
+            System.out.println("Consumer SAL Group Service is down or NULL.");
+            return;
+        }     
+        
+        // For switch events
+        groupListener = FRMConsumerImpl.getNotificationService().registerNotificationListener(groupEventListener);
+        
+        if (null == groupListener) {
+            logger.error("Listener to listen on group data modifcation events");
+            System.out.println("Listener to listen on group data modifcation events.");
+            return;
+        }       
+        
+        commitHandler = new GroupDataCommitHandler();
+        FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, commitHandler);
        }
+       
+    private boolean allocateGroupCaches() {
+        if (this.clusterGroupContainerService == null) {
+            logger.warn("Group: Un-initialized clusterGroupContainerService, can't create cache");
+            return false;
+        }       
+
+        try {
+            clusterGroupContainerService.createCache("frm.originalSwGroupView",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+            clusterGroupContainerService.createCache("frm.installedSwGroupView",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+            clusterGroupContainerService.createCache("frm.inactiveGroups",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+            clusterGroupContainerService.createCache("frm.nodeGroups",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+            
+//TODO for cluster mode
+           /* clusterGroupContainerService.createCache(WORK_STATUS_CACHE,
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, IClusterServices.cacheMode.ASYNC));
+
+            clusterGroupContainerService.createCache(WORK_ORDER_CACHE,
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, IClusterServices.cacheMode.ASYNC));*/
+            
+        } catch (CacheConfigException cce) {            
+            logger.error("Group CacheConfigException");
+            return false;
+            
+        } catch (CacheExistException cce) {
+            logger.error(" Group CacheExistException");           
+        }
+        
+        return true;
+    }
+    
+    private void nonClusterGroupObjectCreate() {
+        originalSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
+        installedSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
+        nodeGroups = new ConcurrentHashMap<Node, List<Group>>();        
+        inactiveGroups = new ConcurrentHashMap<GroupKey, Group>();
+    }
+    
+    @SuppressWarnings({ "unchecked" })
+    private boolean retrieveGroupCaches() {
+        ConcurrentMap<?, ?> map;
+
+        if (this.clusterGroupContainerService == null) {
+            logger.warn("Group: un-initialized clusterGroupContainerService, can't retrieve cache");
+            nonClusterGroupObjectCreate();
+            return false;
+        }       
+
+        map = clusterGroupContainerService.getCache("frm.originalSwGroupView");
+        if (map != null) {
+            originalSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
+        } else {
+            logger.error("Retrieval of cache(originalSwGroupView) failed");
+            return false;
+        }
+
+        map = clusterGroupContainerService.getCache("frm.installedSwGroupView");
+        if (map != null) {
+            installedSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
+        } else {
+            logger.error("Retrieval of cache(installedSwGroupView) failed");
+            return false;
+        }
+
+        map = clusterGroupContainerService.getCache("frm.inactiveGroups");
+        if (map != null) {
+            inactiveGroups = (ConcurrentMap<GroupKey, Group>) map;
+        } else {
+            logger.error("Retrieval of cache(inactiveGroups) failed");
+            return false;
+        }
+
+        map = clusterGroupContainerService.getCache("frm.nodeGroups");
+        if (map != null) {
+            nodeGroups = (ConcurrentMap<Node, List<Group>>) map;
+        } else {
+            logger.error("Retrieval of cache(nodeGroup) failed");
+            return false;
+        }
+        
+        return true;
+    }
+       
+    private boolean cacheStartup() {
+        if (allocateGroupCaches()) {
+            if (retrieveGroupCaches()) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    public Status validateGroup(Group group, FRMUtil.operation operation) {
+        String containerName;
+        String groupName;
+        Iterator<Bucket> bucketIterator;
+        boolean returnResult;
+        Buckets groupBuckets;
+        
+        if (null != group) {
+            containerName = group.getContainerName();
+            
+            if (null == containerName) {
+                containerName = GlobalConstants.DEFAULT.toString();
+            }
+            else if (!FRMUtil.isNameValid(containerName)) {
+                logger.error("Container Name is invalid %s" + containerName);
+                return new Status(StatusCode.BADREQUEST, "Container Name is invalid");
+            }
+            
+            groupName = group.getGroupName();
+            if (!FRMUtil.isNameValid(groupName)) {
+                logger.error("Group Name is invalid %s" + groupName);
+                return new Status(StatusCode.BADREQUEST, "Group Name is invalid");
+            }
+            
+            returnResult = doesGroupEntryExists(group.getKey(), groupName, containerName);
+            
+            if (FRMUtil.operation.ADD == operation && returnResult) {
+                logger.error("Record with same Group Name exists");
+                return new Status(StatusCode.BADREQUEST, "Group record exists");
+            }
+            else if (!returnResult) {
+                logger.error("Group record does not exist");
+                return new Status(StatusCode.BADREQUEST, "Group record does not exist");
+            }
+            
+            if (!(group.getGroupType().getIntValue() >= GroupType.GroupAll.getIntValue() && 
+                    group.getGroupType().getIntValue() <= GroupType.GroupFf.getIntValue())) {
+                logger.error("Invalid Group type %d" + group.getGroupType().getIntValue());
+                return new Status(StatusCode.BADREQUEST, "Invalid Group type");                
+            }
+            
+            groupBuckets = group.getBuckets();
+                    
+            if (null != groupBuckets && null != groupBuckets.getBucket()) {
+                bucketIterator = groupBuckets.getBucket().iterator();
+                
+                while (bucketIterator.hasNext()) {
+                    if(!(FRMUtil.areActionsValid(bucketIterator.next().getActions()))) {
+                        logger.error("Error in action bucket");
+                        return new Status(StatusCode.BADREQUEST, "Invalid Group bucket contents");
+                    }                                
+                }
+            }                
+        }
+        
+        return new Status(StatusCode.SUCCESS);
+        
+    }
+    
+    private boolean doesGroupEntryExists(GroupKey key, String groupName, String containerName) {
+        if (! originalSwGroupView.containsKey(key)) {
+            return false;
+        }
+        
+        for (ConcurrentMap.Entry<GroupKey, Group> entry : originalSwGroupView.entrySet()) {
+            if (entry.getValue().getGroupName().equals(groupName)) {
+                if (entry.getValue().getContainerName().equals(containerName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    
+    /**
+     * Update Group entries to the southbound plugin/inventory and our internal database
+     *
+     * @param path
+     * @param dataObject
+     */
+    private Status updateGroup(InstanceIdentifier<?> path, Group groupUpdateDataObject) {
+        GroupKey groupKey = groupUpdateDataObject.getKey();
+        Status groupOperationStatus = validateGroup(groupUpdateDataObject, FRMUtil.operation.UPDATE);
+        
+        if (!groupOperationStatus.isSuccess()) {
+            logger.error("Group data object validation failed %s" + groupUpdateDataObject.getGroupName());
+            return groupOperationStatus;
+        }
+            
+        originalSwGroupView.remove(groupKey);
+        originalSwGroupView.put(groupKey, groupUpdateDataObject);
+        
+        if (groupUpdateDataObject.isInstall()) {
+            UpdateGroupInputBuilder groupData = new UpdateGroupInputBuilder();
+            //TODO how to get original group and modified group. 
+            
+            if (installedSwGroupView.containsKey(groupKey)) {
+                installedSwGroupView.remove(groupKey);
+            }
+            
+            installedSwGroupView.put(groupKey, groupUpdateDataObject);
+            groupService.updateGroup(groupData.build());
+        }
+        
+        return groupOperationStatus;
+    }
+    
+    /**
+     * Adds Group to the southbound plugin and our internal database
+     *
+     * @param path
+     * @param dataObject
+     */
+    private Status addGroup(InstanceIdentifier<?> path, Group groupAddDataObject) {
+        GroupKey groupKey = groupAddDataObject.getKey();
+        Status groupOperationStatus = validateGroup(groupAddDataObject, FRMUtil.operation.ADD);
+        
+        if (!groupOperationStatus.isSuccess()) {
+            logger.error("Group data object validation failed %s" + groupAddDataObject.getGroupName());
+            return groupOperationStatus;
+        }
+        validateGroup(groupAddDataObject, FRMUtil.operation.ADD);
+        originalSwGroupView.put(groupKey, groupAddDataObject);
+        
+        if (groupAddDataObject.isInstall()) {
+            AddGroupInputBuilder groupData = new AddGroupInputBuilder();
+            groupData.setBuckets(groupAddDataObject.getBuckets());
+            groupData.setContainerName(groupAddDataObject.getContainerName());
+            groupData.setGroupId(groupAddDataObject.getGroupId());
+            groupData.setGroupType(groupAddDataObject.getGroupType());
+            groupData.setNode(groupAddDataObject.getNode());  
+            installedSwGroupView.put(groupKey, groupAddDataObject);
+            groupService.addGroup(groupData.build());
+        }
+        
+        return groupOperationStatus;
+    }
+    
+       private RpcResult<Void> commitToPlugin(internalTransaction transaction) {
+        for(Entry<InstanceIdentifier<?>, Group> entry :transaction.additions.entrySet()) {
+            
+            if (!addGroup(entry.getKey(),entry.getValue()).isSuccess()) {
+                return Rpcs.getRpcResult(false, null, null);
+            }
+        }
+        for(@SuppressWarnings("unused") Entry<InstanceIdentifier<?>, Group> entry :transaction.additions.entrySet()) {
+           
+            if (!updateGroup(entry.getKey(),entry.getValue()).isSuccess()) {
+                return Rpcs.getRpcResult(false, null, null);
+            }
+        }
+        
+        for(InstanceIdentifier<?> removal : transaction.removals) {
+           // removeFlow(removal);
+        }
+        
+        return Rpcs.getRpcResult(true, null, null);
+    }
+    
+    private final class GroupDataCommitHandler implements DataCommitHandler<InstanceIdentifier<?>, DataObject> {
+
+         @SuppressWarnings("unchecked")
+        @Override
+         public DataCommitTransaction requestCommit(DataModification<InstanceIdentifier<?>, DataObject> modification) {
+             // We should verify transaction
+             System.out.println("Coming in FlowDatacommitHandler");
+             internalTransaction transaction = new internalTransaction(modification);
+             transaction.prepareUpdate();
+             return transaction;
+         }
+    }
+
+    private final class internalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
+
+        private final DataModification<InstanceIdentifier<?>, DataObject> modification;
+
+        @Override
+        public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
+            return modification;
+        }
+
+        public internalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
+            this.modification = modification;
+        }
+
+        Map<InstanceIdentifier<?>, Group> additions = new HashMap<>();
+        Map<InstanceIdentifier<?>, Group> updates = new HashMap<>();
+        Set<InstanceIdentifier<?>> removals = new HashSet<>();
+
+        /**
+         * We create a plan which flows will be added, which will be updated and
+         * which will be removed based on our internal state.
+         * 
+         */
+        void prepareUpdate() {
+
+            Set<Entry<InstanceIdentifier<?>, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet();
+            for (Entry<InstanceIdentifier<?>, DataObject> entry : puts) {
+                if (entry.getValue() instanceof Group) {                    
+                    Group group = (Group) entry.getValue();                    
+                    preparePutEntry(entry.getKey(), group);
+                }
+
+            }
+
+            removals = modification.getRemovedConfigurationData();
+        }
+
+        private void preparePutEntry(InstanceIdentifier<?> key, Group group) {
+            
+            Group original = originalSwGroupView.get(key);
+            if (original != null) {
+                // It is update for us
+                
+                updates.put(key, group);               
+            } else {
+                // It is addition for us
+                
+                additions.put(key, group);
+            }
+        }
+
+        /**
+         * We are OK to go with execution of plan
+         * 
+         */
+        @Override
+        public RpcResult<Void> finish() throws IllegalStateException {
+            
+            RpcResult<Void> rpcStatus = commitToPlugin(this);
+            // We return true if internal transaction is successful.
+          //  return Rpcs.getRpcResult(true, null, Collections.emptySet());
+            return rpcStatus;
+        }
+
+        /**
+         * 
+         * We should rollback our preparation
+         * 
+         */
+        @Override
+        public RpcResult<Void> rollback() throws IllegalStateException {
+            // NOOP - we did not modified any internal state during
+            // requestCommit phase
+           // return Rpcs.getRpcResult(true, null, Collections.emptySet());
+            return Rpcs.getRpcResult(true, null, null);
+            
+        }
+        
+    }
+    
+       
+       final class GroupEventListener implements SalGroupListener {
+           
+        List<GroupAdded> addedGroups = new ArrayList<>();
+        List<GroupRemoved> removedGroups = new ArrayList<>();
+        List<GroupUpdated> updatedGroups = new ArrayList<>();
+       
+
+        @Override
+        public void onGroupAdded(GroupAdded notification) {
+            System.out.println("added Group..........................");
+            addedGroups.add(notification);            
+        }
+
+        @Override
+        public void onGroupRemoved(GroupRemoved notification) {
+            // TODO Auto-generated method stub
+            
+        }
+
+        @Override
+        public void onGroupUpdated(GroupUpdated notification) {
+            // TODO Auto-generated method stub
+            
+        }    
+    }
 }
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
new file mode 100644 (file)
index 0000000..262e848
--- /dev/null
@@ -0,0 +1,83 @@
+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<? extends DataObject> path) {
+
+        // Topology and Inventory are operational only
+        return null;
+    }
+
+    override readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+        val type = path.targetType;
+        var DataObject data = null;
+        switch (type) {
+            case Nodes:
+                data = readNodes(path as InstanceIdentifier<Nodes>)
+            case Node:
+                data = readNode(path as InstanceIdentifier<Node>)
+            case NodeConnector:
+                data = readNodeConnector(path as InstanceIdentifier<NodeConnector>)
+        }
+        return data;
+    }
+
+    def DataObject readNodeConnector(InstanceIdentifier<NodeConnector> identifier) {
+        val nodeConnector = identifier.toAdNodeConnector();
+        return constructNodeConnector(nodeConnector)
+    }
+
+    def DataObject readNode(InstanceIdentifier<Node> 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<NodeConnector>(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<Nodes> identifier) {
+        val nodes = switchManager.nodes
+        val nodeList = new ArrayList<Node>(nodes.size)
+        for (node : nodes) {
+            nodeList.add(constructNode(node))
+        }
+        val it = new NodesBuilder();
+        node = nodeList
+        return build()
+
+    }
+}
index 2ae74de108190ab217e4669c20fce67f20a5e1b6..5a4aae36a8c1b8fde1a9805ce981f318b0d1a33a 100644 (file)
@@ -19,7 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.LinkId
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.network.topology.topology.Node
-
+import org.opendaylight.controller.sal.compatibility.InventoryMapping
 class TopologyMapping {
 
     new(TopologyKey path, InstanceIdentifier<Topology> key) {
@@ -29,32 +29,32 @@ class TopologyMapping {
     def Edge toAdTopologyEdge(InstanceIdentifier<Link> identifier) {
         val linkKey = (identifier.path.last as IdentifiableItem<Link,LinkKey>).key;
         val components = linkKey.linkId.value.split("::::");
-        val tail = nodeConnectorFromString(components.get(0).split("::"));
-        val head = nodeConnectorFromString(components.get(1).split("::"));
+        val tail = InventoryMapping.nodeConnectorFromId(components.get(0));
+        val head = InventoryMapping.nodeConnectorFromId(components.get(1));
         return new Edge(tail, head);
     }
 
     def NodeConnector toAdTopologyNodeConnector(InstanceIdentifier<TerminationPoint> identifier) {
         val tpKey = (identifier.path.last as IdentifiableItem<TerminationPoint,TerminationPointKey>).key;
-        return nodeConnectorFromString(tpKey.tpId.value.split("::"));
+        return InventoryMapping.nodeConnectorFromId(tpKey.tpId.value);
     }
 
     def org.opendaylight.controller.sal.core.Node toAdTopologyNode(
         InstanceIdentifier<Node> identifier) {
         val tpKey = (identifier.path.last as IdentifiableItem<Node,NodeKey>).key;
-        return nodeFromString(tpKey.nodeId.value.split("::"));
+        return InventoryMapping.nodeFromNodeId(tpKey.nodeId.value);
     }
     
 
 
     def NodeKey toTopologyNodeKey(org.opendaylight.controller.sal.core.Node node) {
-        val nodeId = new NodeId('''ad-sal:«node.type»::«node.nodeIDString»''')
+        val nodeId = new NodeId(InventoryMapping.toNodeId(node));
         return new NodeKey(nodeId);
     }
 
     def TerminationPointKey toTopologyTerminationPointKey(NodeConnector nc) {
         val node = nc.node;
-        val nodeId = new TpId('''ad-sal:«node.type»::«node.nodeIDString»::«nc.nodeConnectorIDString»''')
+        val nodeId = new TpId(InventoryMapping.toNodeConnectorId(nc))
         return new TerminationPointKey(nodeId);
     }
 
@@ -64,14 +64,4 @@ class TopologyMapping {
         val linkId = new LinkId('''«sourceTp.tpId»::::«destTp.tpId»''')
         return new LinkKey(linkId);
     }
-
-    def NodeConnector nodeConnectorFromString(String[] string) {
-        val node = nodeFromString(string.subList(0,1));
-        return NodeConnector.fromStringNoNode(string.get(2),node);
-    }
-    
-    def org.opendaylight.controller.sal.core.Node nodeFromString(String[] strings) {
-        val type = strings.get(0).substring(6);
-        org.opendaylight.controller.sal.core.Node.fromString(type,strings.get(1))
-    }
 }
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.xtend
new file mode 100644 (file)
index 0000000..0ea9918
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.opendaylight.yangtools.yang.binding.InstanceIdentifier
+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.NodeConnectorId
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef
+
+class InventoryMapping {
+
+    static def org.opendaylight.controller.sal.core.NodeConnector toAdNodeConnector(
+        InstanceIdentifier<NodeConnector> identifier) {
+        val tpKey = (identifier.path.last as IdentifiableItem<NodeConnector,NodeConnectorKey>).key;
+        return nodeConnectorFromId(tpKey.id.value);
+    }
+
+    static def org.opendaylight.controller.sal.core.Node toAdNode(InstanceIdentifier<Node> identifier) {
+        val tpKey = (identifier.path.last as IdentifiableItem<Node,NodeKey>).key;
+        return nodeFromNodeId(tpKey.id.value);
+    }
+    
+    
+     static def NodeRef toNodeRef(org.opendaylight.controller.sal.core.Node node) {
+        val nodeId = new NodeKey(new NodeId(node.toNodeId))
+        val path = InstanceIdentifier.builder().node(Nodes).child(Node,nodeId).toInstance;
+        return new NodeRef(path);
+    }
+
+    static def NodeKey toNodeKey(org.opendaylight.controller.sal.core.Node node) {
+        val nodeId = new NodeId(node.toNodeId)
+        return new NodeKey(nodeId);
+    }
+
+    static def NodeConnectorKey toNodeConnectorKey(org.opendaylight.controller.sal.core.NodeConnector nc) {
+        val nodeId = new NodeConnectorId(nc.toNodeConnectorId)
+        return new NodeConnectorKey(nodeId);
+    }
+
+    static def String toNodeId(org.opendaylight.controller.sal.core.Node node) {
+        '''ad-sal:«node.type»::«node.nodeIDString»'''
+    }
+
+    static def String toNodeConnectorId(org.opendaylight.controller.sal.core.NodeConnector nc) {
+        '''«nc.node.toNodeId»::«nc.nodeConnectorIDString»'''
+    }
+
+    static def org.opendaylight.controller.sal.core.Node nodeFromNodeId(String nodeId) {
+        return nodeFromString(nodeId.split("::"))
+    }
+
+    static def nodeConnectorFromId(String invId) {
+        return nodeConnectorFromString(invId.split("::"));
+    }
+
+    private static def org.opendaylight.controller.sal.core.NodeConnector nodeConnectorFromString(String[] string) {
+        val node = nodeFromString(string.subList(0, 1));
+        return org.opendaylight.controller.sal.core.NodeConnector.fromStringNoNode(string.get(2), node);
+    }
+
+    private static def org.opendaylight.controller.sal.core.Node nodeFromString(String[] strings) {
+        val type = strings.get(0).substring(6);
+        org.opendaylight.controller.sal.core.Node.fromString(type, strings.get(1))
+    }
+
+}
index 6267c048a6d678fc0c6bbe6af5fe33b0485a847a..ae427455a415344b75d5db2c7d1e1e13049d37d1 100644 (file)
@@ -5,10 +5,10 @@ import java.util.concurrent.Future;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
 import org.opendaylight.controller.sal.common.util.Futures;
 import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.compatibility.InventoryMapping;
 import org.opendaylight.controller.sal.compatibility.NodeMapping;
 import org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils;
 import org.opendaylight.controller.sal.core.ConstructionException;
-import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.flowprogrammer.Flow;
 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
@@ -18,6 +18,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.Flow
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,58 +33,52 @@ public class FlowServiceAdapter implements SalFlowService, IFlowProgrammerListen
     private NotificationProviderService publish;
 
     @Override
-    public void flowRemoved(Node node, Flow flow) {
+    public void flowRemoved(org.opendaylight.controller.sal.core.Node node, Flow flow) {
         FlowRemovedBuilder flowRemovedBuilder = new FlowRemovedBuilder();
-        flowRemovedBuilder.setNode(NodeMapping.toNodeRef(node));
+        flowRemovedBuilder.setNode(InventoryMapping.toNodeRef(node));
         publish.publish(flowRemovedBuilder.build());
     }
 
     @Override
-    public void flowErrorReported(Node node, long rid, Object err) {
+    public void flowErrorReported(org.opendaylight.controller.sal.core.Node node, long rid, Object err) {
         // TODO Auto-generated method stub
 
     }
 
     @Override
     public Future<RpcResult<Void>> addFlow(AddFlowInput input) {
-        try {
-            Flow flow = ToSalConversionsUtils.toFlow(input);
-            Node node = NodeMapping.toADNode(input.getNode());
-            Status status = delegate.addFlowAsync(node, flow);
-            Void rpcResultType = null;
-            return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
-        } catch (ConstructionException e) {
-            LOG.error(e.getMessage());
-        }
-        return null;
+
+        Flow flow = ToSalConversionsUtils.toFlow(input);
+        @SuppressWarnings("unchecked")
+        org.opendaylight.controller.sal.core.Node node = InventoryMapping.toAdNode((InstanceIdentifier<Node>) input
+                .getNode().getValue());
+        Status status = delegate.addFlowAsync(node, flow);
+        Void rpcResultType = null;
+        return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
     }
 
     @Override
     public Future<RpcResult<Void>> removeFlow(RemoveFlowInput input) {
-        try {
-            Flow flow = ToSalConversionsUtils.toFlow(input);
-            Node node = NodeMapping.toADNode(input.getNode());
-            Status status = delegate.removeFlowAsync(node, flow);
-            Void rpcResultType = null;
-            return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
-        } catch (ConstructionException e) {
-            LOG.error(e.getMessage());
-        }
-        return null;
+
+        Flow flow = ToSalConversionsUtils.toFlow(input);
+        @SuppressWarnings("unchecked")
+        org.opendaylight.controller.sal.core.Node node = InventoryMapping.toAdNode((InstanceIdentifier<Node>) input
+                .getNode().getValue());
+        Status status = delegate.removeFlowAsync(node, flow);
+        Void rpcResultType = null;
+        return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
+
     }
 
     @Override
     public Future<RpcResult<Void>> updateFlow(UpdateFlowInput input) {
-        try {
-            Node node = NodeMapping.toADNode(input.getNode());
-            Flow originalFlow = ToSalConversionsUtils.toFlow(input.getOriginalFlow());
-            Flow updatedFlow = ToSalConversionsUtils.toFlow(input.getUpdatedFlow());
-            Status status = delegate.modifyFlowAsync(node, originalFlow, updatedFlow);
-            Void rpcResultType = null;
-            return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
-        } catch (ConstructionException e) {
-            LOG.error(e.getMessage());
-        }
-        return null;
+        @SuppressWarnings("unchecked")
+        org.opendaylight.controller.sal.core.Node node = InventoryMapping.toAdNode((InstanceIdentifier<Node>) input
+                .getNode().getValue());
+        Flow originalFlow = ToSalConversionsUtils.toFlow(input.getOriginalFlow());
+        Flow updatedFlow = ToSalConversionsUtils.toFlow(input.getUpdatedFlow());
+        Status status = delegate.modifyFlowAsync(node, originalFlow, updatedFlow);
+        Void rpcResultType = null;
+        return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
     }
 }
index 1c8b772d72287e2102b2ac156fb7d13c715edded..c37cce0837a00e6214ac8230d913bcb335781754 100644 (file)
@@ -1,4 +1,11 @@
-package org.opendaylight.controller.sal.compability;
+/*
+ * 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.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
index 2b344c0c80bf7fa6b7c1b1e02a4f91505dcd7ad9..e251dd02a0baf299e596cc9334459cd2342d70ac 100644 (file)
@@ -1,4 +1,11 @@
-package org.opendaylight.controller.sal.compability;
+/*
+ * 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.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
index 8e77d26ea4afe61eea770873fea7d05a7bbd4818..03e36924e77968ab2815f9b391055945a62dc69b 100644 (file)
@@ -41,6 +41,10 @@ module opendaylight-group-types {
             type string; 
         }
         
+        leaf barrier {
+            type boolean; 
+        }       
+        
         container buckets {
             list bucket {
                 key "order";
index 99d4588cbc85b2491c3f06a346014b9072130b2e..20353af7852bd0b46834e7ad61364b222ec89640 100644 (file)
@@ -85,6 +85,17 @@ module opendaylight-meter-types {
             type meter-id;
         }
         
+        leaf install {
+            type boolean; 
+        }
+        leaf meter-name {
+            type string;
+        }
+        
+        leaf container-name {
+            type string; 
+        }
+        
         container meter-band-headers {
             list meter-band-header {
                 key "order";
index 6a83f75959edd624c35b43bf9de08290197ba89b..8338d185cc36abbbbc34613fe1e3df84d805550a 100644 (file)
@@ -20,10 +20,10 @@ module opendaylight-port-types {
     }
     
     typedef port-state {
-            type enumeration {
-                enum link-down;
-                enum blocked;
-                enum live;
+        type enumeration {
+            enum link-down;
+            enum blocked;
+            enum live;
         }
     }
 
@@ -125,7 +125,7 @@ module opendaylight-port-types {
                 uses common-port;
                 
                 leaf mask {
-                    type uint32;
+                    type port-config;
                     description "Bitmap of OFPPC-* flags to be changed";
                 }      
                 
@@ -135,7 +135,11 @@ module opendaylight-port-types {
             
                 leaf port-name {
                     type string; 
-                }                
+                } 
+
+                leaf barrier {
+                    type boolean; 
+                } 
             }            
         }    
     }
index 579ce70c626227234f7d07fbe7a2451b9fb4f81c..111d3d60faaf29fcfff6e93f05be3bad10b089e5 100644 (file)
@@ -11,11 +11,9 @@ module meter-management {
     }
 
     grouping meter-entry {
-    
         leaf node {
             type inv:node-ref;
         }
-        
         uses meter:meter;
     }   
      
diff --git a/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-capable-transaction.yang b/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-capable-transaction.yang
new file mode 100644 (file)
index 0000000..483c0c9
--- /dev/null
@@ -0,0 +1,46 @@
+module flow-capable-transaction {
+       namespace "urn:opendaylight:flow:transaction";
+    prefix type;
+
+    import opendaylight-inventory {prefix inv; revision-date "2013-08-19";}
+    import yang-ext {prefix ext; revision-date "2013-07-09";}
+    
+    revision "2013-11-03" {
+        description "Initial revision";
+    }
+
+    typedef transaction-id {
+       type uint64;
+    }
+    
+    grouping transaction-aware {
+        leaf transaction-id {
+            type transaction-id;
+        }
+    }
+
+    rpc get-next-transaction-id {
+        input {
+            leaf node {
+                ext:context-reference "inv:node-context";
+                type inv:node-ref;
+            }
+        }
+        output {
+               uses transaction-aware;
+        }
+    }
+
+    // Barier request?
+    rpc finish-transaction {
+        input {
+            leaf node {
+                ext:context-reference "inv:node-context";
+                type inv:node-ref;
+            }
+            leaf transaction-id {
+                type transaction-id;
+            }
+        }
+    }
+}
\ No newline at end of file
index fba1c0829c96b032f4094f82df13198b8a124730..bb010353280b8fa281c0901dde2e8d771f62ac13 100644 (file)
@@ -6,14 +6,14 @@ module sal-group {
     import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
     import ietf-inet-types {prefix inet;revision-date 2010-09-24;}
     import opendaylight-group-types {prefix group-type;revision-date 2013-10-18;}
+    import flow-capable-transaction {prefix tr;}
 
     revision "2013-09-18" {
         description "Initial revision of group service";
     }        
     
     grouping node-group {
-        uses "inv:node-context-ref";
-        
+        uses "inv:node-context-ref";        
         uses group-type:group;
     }
 
@@ -32,18 +32,30 @@ module sal-group {
     rpc add-group {
         input {
             uses node-group;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }
 
     rpc remove-group {
         input {
             uses group-update;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }
 
     rpc update-group {
         input {
             uses group-update;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     } 
 
index d3777ff3320468bc55b5307ae6bdff61d016ae23..c187181e5e89099cc4720031cd3fa888629d69a2 100644 (file)
@@ -5,6 +5,7 @@ module sal-meter {
     import yang-ext {prefix ext; revision-date "2013-07-09";}
     import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
     import opendaylight-meter-types {prefix meter-type;revision-date "2013-09-18";}
+    import flow-capable-transaction {prefix tr;}
 
     revision "2013-09-18" {
         description "Initial revision of meter service";
@@ -31,18 +32,30 @@ module sal-meter {
     rpc add-meter {
         input {
             uses node-meter;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }
 
     rpc remove-meter {
         input {
             uses node-meter;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }
 
     rpc update-meter {
         input {
             uses meter-update;
+            uses tr:transaction-aware;            
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }
         
diff --git a/opendaylight/md-sal/model/model-flow-service/src/main/yang/port-service.yang b/opendaylight/md-sal/model/model-flow-service/src/main/yang/port-service.yang
new file mode 100644 (file)
index 0000000..df527ef
--- /dev/null
@@ -0,0 +1,46 @@
+module sal-port {
+    namespace "urn:opendaylight:port:service";
+    prefix port;
+
+    import yang-ext {prefix ext; revision-date "2013-07-09";}
+    import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
+    import opendaylight-port-types {prefix port-type;revision-date "2013-09-25";}
+
+    revision "2013-11-07" {
+        description "Initial revision of port service";
+    }        
+    
+    grouping node-port {
+        uses "inv:node-context-ref";
+        
+        uses port-type:ofp-port-mod;
+    }
+
+    /** Base configuration structure **/
+    grouping port-update {
+        uses "inv:node-context-ref";
+
+        container original-port {
+            uses port-type:ofp-port-mod;
+        }
+        container updated-port {
+            uses port-type:ofp-port-mod;
+        }
+    }
+
+    rpc update-port {
+        input {
+            uses port-update;
+        }
+    }
+     
+    rpc get-port {
+        output {
+            uses port-type:flow-capable-port;
+        }
+    }  
+    
+    notification port-removed {
+        uses node-port;
+    }
+}
\ No newline at end of file
index e740f961d5a83d234342a94aa151d476f001b9cf..3a6f20f77d9554dda1812311e7510414d1a6658a 100644 (file)
@@ -5,13 +5,15 @@ module sal-table {
     import yang-ext {prefix ext; revision-date "2013-07-09";}
     import opendaylight-inventory {prefix inv;revision-date "2013-08-19";}
     import opendaylight-table-types {prefix table-type;revision-date "2013-10-26";}
+    import flow-capable-transaction {prefix tr;}
 
     revision "2013-10-26" {
         description "Initial revision of table service";
-    }        
+    }
     
     /** Base configuration structure **/
     grouping table-update {
+        uses "inv:node-context-ref";
         container original-table {
             uses table-type:table-features;
         }
@@ -22,11 +24,11 @@ module sal-table {
     
     rpc update-table {
         input {
-            leaf node {
-                ext:context-reference "inv:node-context";
-                type inv:node-ref;
-            }
             uses table-update;
+            uses tr:transaction-aware;
+        }
+        output {
+            uses tr:transaction-aware;
         }
     }     
 }
\ No newline at end of file
index c1dccdf5320a488a6b4c38a7464eb10e4d53af24..cfbd4f7b719e0f5217fbee4dba444b38ca0e9c7a 100644 (file)
@@ -38,6 +38,7 @@
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
             <version>${osgi.core.version}</version>
+            <scope>provided</scope>
         </dependency>
     </dependencies>
 </project>
index b8b37af58ee42eec8f1ca2b50763013f20c935f8..9ca025b393911d11ede993899a32e2488939c8a2 100644 (file)
                 <configuration>
                     <instructions>
                         <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                        <Export-package>
+                            org.opendaylight.controller.sal.binding.spi.*,
+                        </Export-package>
                         <Private-Package>
                             org.opendaylight.controller.config.yang.md.sal.binding.impl,
-                            org.opendaylight.controller.sal.binding.spi,
-                            org.opendaylight.controller.sal.binding.spi.*,
                             org.opendaylight.controller.sal.binding.impl,
                             org.opendaylight.controller.sal.binding.impl.*,
+                            org.opendaylight.controller.sal.binding.codegen,
                             org.opendaylight.controller.sal.binding.codegen.*,
                         </Private-Package>
                     </instructions>
index 9356ecda88315a0d055d2279070e407321422f38..6ed63b21ddb4a67d3485c008c4417d19131367c9 100644 (file)
@@ -5,23 +5,13 @@ import org.opendaylight.controller.sal.binding.api.data.DataChangeListener
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService
 import org.opendaylight.yangtools.yang.binding.DataObject
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction.DataTransactionListener
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
-import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification
 import org.opendaylight.controller.md.sal.common.api.data.DataReader
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration
 import org.opendaylight.yangtools.concepts.ListenerRegistration
-import static extension org.opendaylight.controller.sal.binding.impl.util.MapUtils.*;
-import java.util.Collection
-import java.util.Map.Entry
-import java.util.HashSet
-import java.util.Set
 import com.google.common.collect.Multimap
 import static com.google.common.base.Preconditions.*;
 import java.util.List
-import java.util.LinkedList
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
 import com.google.common.collect.HashMultimap
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Callable
@@ -30,15 +20,17 @@ import org.opendaylight.controller.sal.common.util.Rpcs
 import java.util.Collections
 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction
 import java.util.ArrayList
-import org.opendaylight.controller.sal.common.util.RpcErrors
+import org.opendaylight.controller.sal.binding.impl.util.BindingAwareDataReaderRouter
+import org.opendaylight.yangtools.concepts.CompositeObjectRegistration
+import java.util.Arrays
 
 class DataBrokerImpl extends DeprecatedDataAPISupport implements DataProviderService {
 
     @Property
     var ExecutorService executor;
 
-    Multimap<InstanceIdentifier, DataReaderRegistration> configReaders = HashMultimap.create();
-    Multimap<InstanceIdentifier, DataReaderRegistration> operationalReaders = HashMultimap.create();
+    val dataReadRouter = new BindingAwareDataReaderRouter;
+
     Multimap<InstanceIdentifier, DataChangeListenerRegistration> listeners = HashMultimap.create();
     Multimap<InstanceIdentifier, DataCommitHandlerRegistration> commitHandlers = HashMultimap.create();
 
@@ -47,13 +39,11 @@ class DataBrokerImpl extends DeprecatedDataAPISupport implements DataProviderSer
     }
 
     override readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
-        val readers = configReaders.getAllChildren(path);
-        return readers.readConfiguration(path);
+        return dataReadRouter.readConfigurationData(path);
     }
 
     override readOperationalData(InstanceIdentifier<? extends DataObject> path) {
-        val readers = operationalReaders.getAllChildren(path);
-        return readers.readOperational(path);
+        return dataReadRouter.readOperationalData(path);
     }
 
     override registerCommitHandler(InstanceIdentifier<? extends DataObject> path,
@@ -69,20 +59,12 @@ class DataBrokerImpl extends DeprecatedDataAPISupport implements DataProviderSer
         return reg;
     }
 
-    override registerDataReader(InstanceIdentifier<? extends DataObject> path,
-        DataReader<InstanceIdentifier<? extends DataObject>, DataObject> provider) {
-        val ret = new DataReaderRegistration(provider, this);
-        ret.paths.add(path);
-        configReaders.put(path, ret);
-        operationalReaders.put(path, ret);
-        return ret;
-    }
-
-    protected def removeReader(DataReaderRegistration reader) {
-        for (path : reader.paths) {
-            operationalReaders.remove(path, reader);
-            configReaders.remove(path, reader);
-        }
+    override registerDataReader(InstanceIdentifier<? extends DataObject> path,DataReader<InstanceIdentifier<? extends DataObject>,DataObject> reader) {
+        
+        val confReg = dataReadRouter.registerConfigurationReader(path,reader);
+        val dataReg = dataReadRouter.registerOperationalReader(path,reader);
+        
+        return new CompositeObjectRegistration(reader,Arrays.asList(confReg,dataReg));
     }
 
     protected def removeListener(DataChangeListenerRegistration registration) {
@@ -92,39 +74,8 @@ class DataBrokerImpl extends DeprecatedDataAPISupport implements DataProviderSer
     protected def removeCommitHandler(DataCommitHandlerRegistration registration) {
         commitHandlers.remove(registration.path, registration);
     }
-
-    protected def DataObject readConfiguration(
-        Collection<Entry<? extends InstanceIdentifier, ? extends DataReaderRegistration>> entries,
-        InstanceIdentifier<? extends DataObject> path) {
-
-        val List<DataObject> partialResults = new LinkedList();
-        for (entry : entries) {
-            partialResults.add(entry.value.instance.readConfigurationData(path))
-        }
-        return merge(path, partialResults);
-    }
-
-    protected def DataObject readOperational(
-        Collection<Entry<? extends InstanceIdentifier, ? extends DataReaderRegistration>> entries,
-        InstanceIdentifier<? extends DataObject> path) {
-
-        val List<DataObject> partialResults = new LinkedList();
-        for (entry : entries) {
-            partialResults.add(entry.value.instance.readOperationalData(path))
-        }
-        return merge(path, partialResults);
-    }
-
-    protected def DataObject merge(InstanceIdentifier<? extends DataObject> identifier, List<DataObject> objects) {
-
-        // FIXME: implement real merge
-        if (objects.size > 0) {
-            return objects.get(0);
-        }
-    }
     
     protected def getActiveCommitHandlers() {
-        
         return commitHandlers.entries.map[ value.instance].toSet
     }
 
@@ -137,26 +88,6 @@ class DataBrokerImpl extends DeprecatedDataAPISupport implements DataProviderSer
 
 }
 
-package class DataReaderRegistration extends //
-AbstractObjectRegistration<DataReader<InstanceIdentifier<? extends DataObject>, DataObject>> {
-
-    DataBrokerImpl dataBroker;
-
-    @Property
-    val Set<InstanceIdentifier<? extends DataObject>> paths;
-
-    new(DataReader<InstanceIdentifier<? extends DataObject>, DataObject> instance, DataBrokerImpl broker) {
-        super(instance)
-        dataBroker = broker;
-        _paths = new HashSet();
-    }
-
-    override protected removeRegistration() {
-        dataBroker.removeReader(this);
-    }
-
-}
-
 package class DataChangeListenerRegistration extends AbstractObjectRegistration<DataChangeListener> implements ListenerRegistration<DataChangeListener> {
 
     DataBrokerImpl dataBroker;
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataProviderContext.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataProviderContext.xtend
deleted file mode 100644 (file)
index 398a219..0000000
+++ /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.sal.binding.impl
-
-import org.opendaylight.controller.sal.common.DataStoreIdentifier
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
-
-class DataProviderContext {
-
-    @Property
-    var DataStoreIdentifier identifier;
-    @Property
-    var RuntimeDataProvider provider;
-}
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;
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.xtend
new file mode 100644 (file)
index 0000000..f586a8b
--- /dev/null
@@ -0,0 +1,13 @@
+package org.opendaylight.controller.sal.binding.impl.util
+
+import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.opendaylight.yangtools.yang.binding.DataObject
+
+class BindingAwareDataReaderRouter extends AbstractDataReadRouter<InstanceIdentifier<? extends DataObject>, DataObject> {
+    
+    override protected merge(InstanceIdentifier<? extends DataObject> path, Iterable<DataObject> data) {
+        return data.iterator.next;
+    }
+    
+}
\ No newline at end of file
index 0fea50b777f14dc31275f9b75c7d1bf4622f6ece..30a607d95b6a025619e836dd26097707704ecae3 100644 (file)
@@ -13,19 +13,75 @@ import java.util.Set;
 // FIXME: After 0.6 Release of YANGTools refactor to use Path marker interface for arguments.
 // import org.opendaylight.yangtools.concepts.Path;
 
+public interface DataChange<P/* extends Path<P> */, D> {
 
-public interface DataChange<P/* extends Path<P> */,D> {
+    /**
+     * Returns a map of paths and newly created objects
+     * 
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedOperationalData();
 
-    Map<P,D> getCreatedOperationalData();
+    /**
+     * Returns a map of paths and newly created objects
+     * 
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedConfigurationData();
 
-    Map<P,D> getUpdatedOperationalData();
+    /**
+     * Returns a map of paths and respective updated objects after update.
+     * 
+     * Original state of the object is in
+     * {@link #getOriginalOperationalData()}
+     * 
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedOperationalData();
 
-    Set<P> getRemovedOperationalData();
+    /**
+     * Returns a map of paths and respective updated objects after update.
+     * 
+     * Original state of the object is in
+     * {@link #getOriginalConfigurationData()}
+     * 
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedConfigurationData();
 
-    Map<P,D> getCreatedConfigurationData();
 
-    Map<P,D> getUpdatedConfigurationData();
 
+    /**
+     * Returns a set of paths of removed objects.
+     * 
+     * Original state of the object is in
+     * {@link #getOriginalConfigurationData()}
+     * 
+     * @return map of paths and newly created objects
+     */
     Set<P> getRemovedConfigurationData();
 
+    /**
+     * Returns a set of paths of removed objects.
+     * 
+     * Original state of the object is in
+     * {@link #getOriginalOperationalData()}
+     * 
+     * @return map of paths and newly created objects
+     */
+    Set<P> getRemovedOperationalData();
+
+    /**
+     * Return a map of paths and original state of updated and removed objectd.
+     * 
+     * @return map of paths and original state of updated and removed objectd.
+     */
+    Map<P, D> getOriginalConfigurationData();
+
+    /**
+     * Return a map of paths and original state of updated and removed objectd.
+     * 
+     * @return map of paths and original state of updated and removed objectd.
+     */
+    Map<P, D> getOriginalOperationalData();
 }
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 85e3d8f57ca4a6a2de3913b4363ba5e1fb0af651..90de13d15e8229238f36c5ec59b1180babcff1ae 100644 (file)
@@ -84,7 +84,7 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
  */
 public interface DataCommitHandler<P/* extends Path<P> */,D> {
 
-
+    
     DataCommitTransaction<P, D> requestCommit(DataModification<P,D> modification);
 
     public interface DataCommitTransaction<P/* extends Path<P> */,D> {
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.
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java
new file mode 100644 (file)
index 0000000..f448d4e
--- /dev/null
@@ -0,0 +1,16 @@
+package org.opendaylight.controller.md.sal.common.api.data;
+
+public interface DataStore<P, D> extends //
+        DataReader<P, D>, //
+        DataModificationTransactionFactory<P, D> {
+
+    @Override
+    public DataModification<P, D> beginTransaction();
+
+    @Override
+    public D readConfigurationData(P path);
+
+    @Override
+    public D readOperationalData(P path);
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java
new file mode 100644 (file)
index 0000000..afe9e99
--- /dev/null
@@ -0,0 +1,10 @@
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+
+public interface Route<C,P> extends Immutable {
+
+    C getType();
+    
+    P getPath();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java
new file mode 100644 (file)
index 0000000..994f65b
--- /dev/null
@@ -0,0 +1,8 @@
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.EventListener;
+
+public interface RouteChangeListener<C,P> extends EventListener {
+
+    void onRouteChange(RouteChange<C, P> change);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java
new file mode 100644 (file)
index 0000000..89851c9
--- /dev/null
@@ -0,0 +1,8 @@
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public interface RouteChangePublisher<C,P> {
+
+    ListenerRegistration<RouteChangeListener<C,P>> registerRouteChangeListener(RouteChangeListener<C,P> listener);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java
new file mode 100644 (file)
index 0000000..8d0a90c
--- /dev/null
@@ -0,0 +1,10 @@
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface Router<C,P,D> extends //
+        RouteChangePublisher<C, P> {
+
+    Map<C, Set<P>> getAnnouncedPaths();
+}
index 0c2344a23e916e45ab43ace0a9e0cd3866cb266a..3bd51ec7d8b68dabe567857101a098cf2d2f4928 100644 (file)
         <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
           <instructions>
-            <Export-Package>org.opendaylight.controller.md.sal.common.impl</Export-Package>
+            <Export-Package>
+                org.opendaylight.controller.md.sal.common.impl,
+                org.opendaylight.controller.md.sal.common.impl.*
+            </Export-Package>
           </instructions>
         </configuration>
       </plugin>
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;
+    }
 }
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/AbstractDataReadRouter.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/AbstractDataReadRouter.java
new file mode 100644 (file)
index 0000000..f83c61f
--- /dev/null
@@ -0,0 +1,187 @@
+package org.opendaylight.controller.md.sal.common.impl.routing;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Base abstract implementation of DataReadRouter, which performs
+ * a read operation on multiple data readers and then merges result.
+ * 
+ * @param <P>
+ * @param <D>
+ */
+public abstract class AbstractDataReadRouter<P extends Path<?>, D> implements DataReader<P, D> {
+
+    Multimap<P, DataReaderRegistration<P, D>> configReaders = HashMultimap.create();
+    Multimap<P, DataReaderRegistration<P, D>> operationalReaders = HashMultimap.create();
+
+    @Override
+    public D readConfigurationData(P path) {
+        FluentIterable<D> dataBits = FluentIterable //
+                .from(getReaders(configReaders, path)).transform(configurationRead(path));
+        return merge(path,dataBits);
+    }
+
+    @Override
+    public D readOperationalData(P path) {
+        FluentIterable<D> dataBits = FluentIterable //
+                .from(getReaders(configReaders, path)).transform(operationalRead(path));
+        return merge(path,dataBits);
+
+    }
+
+    /**
+     * Merges data readed by reader instances from specified path
+     * 
+     * @param path Path on which read was performed
+     * @param data Data which was returned by read operation.
+     * @return Merged result.
+     */
+    protected abstract D merge(P path,Iterable<D> data);
+
+    /**
+     * Returns a function which performs configuration read for supplied path
+     * 
+     * @param path
+     * @return function which performs configuration read for supplied path
+     */
+    
+    private Function<DataReader<P, D>, D> configurationRead(final P path) {
+        return new Function<DataReader<P, D>, D>() {
+            @Override
+            public D apply(DataReader<P, D> input) {
+                return input.readConfigurationData(path);
+            }
+        };
+    }
+
+    /**
+     * Returns a function which performs operational read for supplied path
+     * 
+     * @param path
+     * @return function which performs operational read for supplied path
+     */
+    private Function<DataReader<P, D>, D> operationalRead(final P path) {
+        return new Function<DataReader<P, D>, D>() {
+            @Override
+            public D apply(DataReader<P, D> input) {
+                return input.readConfigurationData(path);
+            }
+        };
+    }
+
+    // Registrations
+
+    /**
+     * Register's a reader for operational data.
+     * 
+     * @param path Path which is served by this reader
+     * @param reader Reader instance which is responsible for reading particular subpath.
+     * @return 
+     */
+    public Registration<DataReader<P, D>> registerOperationalReader(P path, DataReader<P, D> reader) {
+        OperationalDataReaderRegistration<P, D> ret = new OperationalDataReaderRegistration<>(path, reader);
+        operationalReaders.put(path, ret);
+        return ret;
+    }
+
+    public Registration<DataReader<P, D>> registerConfigurationReader(P path, DataReader<P, D> reader) {
+        ConfigurationDataReaderRegistration<P, D> ret = new ConfigurationDataReaderRegistration<>(path, reader);
+        configReaders.put(path, ret);
+        return ret;
+    }
+
+    Iterable<DataReader<P, D>> getOperationalReaders(P path) {
+        return getReaders(operationalReaders, path);
+    }
+
+    Iterable<DataReader<P, D>> getConfigurationReaders(P path) {
+        return getReaders(configReaders, path);
+    }
+
+    private Iterable<DataReader<P, D>> getReaders(Multimap<P, DataReaderRegistration<P, D>> readerMap, P path) {
+        return FluentIterable
+            .from(readerMap.entries()) //
+            .filter(affects(path)) //
+            .transform(retrieveInstance());
+    }
+
+    private void removeRegistration(OperationalDataReaderRegistration<?, ?> registration) {
+        operationalReaders.remove(registration.getKey(), registration);
+    }
+
+    private void removeRegistration(ConfigurationDataReaderRegistration<?, ?> registration) {
+        configReaders.remove(registration.getKey(), registration);
+    }
+
+    private Function<? super Entry<P, DataReaderRegistration<P, D>>, DataReader<P, D>> retrieveInstance() {
+        return new Function<Entry<P, DataReaderRegistration<P, D>>, DataReader<P,D>>() {
+            @Override
+            public DataReader<P, D> apply(Entry<P, DataReaderRegistration<P, D>> input) {
+                return input.getValue().getInstance();
+            }
+        };
+    }
+
+    private Predicate<? super Entry<P, DataReaderRegistration<P, D>>> affects(final P path) {
+        
+        return new Predicate<Entry<P, DataReaderRegistration<P, D>>>() {
+            
+            @Override
+            public boolean apply(Entry<P, DataReaderRegistration<P, D>> input) {
+                final Path key = input.getKey();
+                return key.contains(path) || ((Path) path).contains(key);
+            }
+            
+        };
+    }
+
+    private class ConfigurationDataReaderRegistration<P extends Path<?>, D> extends DataReaderRegistration<P, D> {
+
+        public ConfigurationDataReaderRegistration(P key, DataReader<P, D> instance) {
+            super(key, instance);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            AbstractDataReadRouter.this.removeRegistration(this);
+        }
+    }
+
+    private class OperationalDataReaderRegistration<P extends Path<?>, D> extends DataReaderRegistration<P, D> {
+
+        public OperationalDataReaderRegistration(P key, DataReader<P, D> instance) {
+            super(key, instance);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            AbstractDataReadRouter.this.removeRegistration(this);
+        }
+    }
+
+    private abstract static class DataReaderRegistration<P extends Path<?>, D> extends
+            AbstractObjectRegistration<DataReader<P, D>> {
+
+        private final P key;
+
+        public P getKey() {
+            return this.key;
+        }
+
+        public DataReaderRegistration(P key, DataReader<P, D> instance) {
+            super(instance);
+            this.key = key;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcConsumptionRegistry.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcConsumptionRegistry.java
new file mode 100644 (file)
index 0000000..c19ee1a
--- /dev/null
@@ -0,0 +1,22 @@
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public interface RpcConsumptionRegistry {
+    /**
+     * Sends an RPC to other components registered to the broker.
+     * 
+     * @see RpcImplementation
+     * @param rpc
+     *            Name of RPC
+     * @param input
+     *            Input data to the RPC
+     * @return Result of the RPC call
+     */
+    Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java
new file mode 100644 (file)
index 0000000..c326bab
--- /dev/null
@@ -0,0 +1,33 @@
+package org.opendaylight.controller.sal.core.api;
+
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface RpcProvisionRegistry {
+
+    /**
+     * Registers an implementation of the rpc.
+     * 
+     * <p>
+     * The registered rpc functionality will be available to all other
+     * consumers and providers registered to the broker, which are aware of
+     * the {@link QName} assigned to the rpc.
+     * 
+     * <p>
+     * There is no assumption that rpc type is in the set returned by
+     * invoking {@link RpcImplementation#getSupportedRpcs()}. This allows
+     * for dynamic rpc implementations.
+     * 
+     * @param rpcType
+     *            Name of Rpc
+     * @param implementation
+     *            Provider's Implementation of the RPC functionality
+     * @throws IllegalArgumentException
+     *             If the name of RPC is invalid
+     */
+    RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+            throws IllegalArgumentException;
+
+    RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation);
+}
index 3024c89d615b4723041fe0707f5459a08f698b1b..20fa29dceb7cd807ca78c2650d357f5a30bb0cb1 100644 (file)
@@ -10,8 +10,10 @@ package org.opendaylight.controller.sal.core.api.data;
 import org.opendaylight.controller.md.sal.common.api.data.DataProvisionService;
 import org.opendaylight.controller.sal.common.DataStoreIdentifier;
 import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;;
 
 public interface DataProviderService extends 
     DataBrokerService, //
@@ -54,6 +56,11 @@ public interface DataProviderService extends
      */
     void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);
 
+    
+    Registration<DataReader<InstanceIdentifier, CompositeNode>> registerConfigurationReader(InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader);
+
+    Registration<DataReader<InstanceIdentifier, CompositeNode>> registerOperationalReader(InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader);
+    
     public interface DataRefresher extends Provider.ProviderFunctionality {
 
         /**
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionInstance.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionInstance.java
new file mode 100644 (file)
index 0000000..8f6a5d0
--- /dev/null
@@ -0,0 +1,8 @@
+package org.opendaylight.controller.sal.core.api.mount;
+
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+
+public interface MountProvisionInstance extends MountInstance, NotificationPublishService, RpcProvisionRegistry {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java
new file mode 100644 (file)
index 0000000..fade7d3
--- /dev/null
@@ -0,0 +1,13 @@
+package org.opendaylight.controller.sal.core.api.mount;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public interface MountProvisionService extends MountService {
+
+    @Override
+    public MountProvisionInstance getMountPoint(InstanceIdentifier path);
+    
+    MountProvisionInstance createMountPoint(InstanceIdentifier path);
+    
+    MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
+}
index 678728ab3b86b8d0883dce0f613f2cd42b6e5129..9383a9e2ff50ee2149d1ce034ed6f195dfb0e7e6 100644 (file)
             <groupId>org.opendaylight.controller</groupId>\r
             <artifactId>sal-common-util</artifactId>\r
             <version>1.0-SNAPSHOT</version>\r
+        </dependency>\r
+                <dependency>\r
+            <groupId>org.opendaylight.controller</groupId>\r
+            <artifactId>sal-common-impl</artifactId>\r
+            <version>1.0-SNAPSHOT</version>\r
         </dependency>\r
         <dependency>\r
             <groupId>org.opendaylight.controller</groupId>\r
@@ -60,7 +65,7 @@
                         <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>\r
                         <Bundle-Activator>org.opendaylight.controller.sal.dom.broker.BrokerActivator</Bundle-Activator>\r
                         <Private-Package>\r
-                            org.opendaylight.controller.sal.dom.broker,\r
+                            org.opendaylight.controller.sal.dom.broker.*\r
                         </Private-Package>\r
                     </instructions>\r
                 </configuration>\r
index 83dda5902dee4e6fd819debabcaecd12ca0413e8..855ad9bd328d19c79b30e09ba4e485e0d1918044 100644 (file)
@@ -7,29 +7,28 @@
  */
 package org.opendaylight.controller.sal.dom.broker;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.BrokerService;
-import org.opendaylight.controller.sal.core.api.Consumer;
-import org.opendaylight.controller.sal.core.api.Provider;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.sal.core.spi.BrokerModule;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.osgi.framework.BundleContext;
-import org.slf4j.LoggerFactory;
+import java.util.Collections
+import java.util.HashMap
+import java.util.HashSet
+import java.util.Map
+import java.util.Set
+import java.util.concurrent.Callable
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.Future
+import org.opendaylight.controller.sal.core.api.Broker
+import org.opendaylight.controller.sal.core.api.BrokerService
+import org.opendaylight.controller.sal.core.api.Consumer
+import org.opendaylight.controller.sal.core.api.Provider
+import org.opendaylight.controller.sal.core.spi.BrokerModule
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.common.RpcResult
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import org.osgi.framework.BundleContext
+import org.slf4j.LoggerFactory
+import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
 import org.opendaylight.yangtools.concepts.ListenerRegistration
 import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
-import org.opendaylight.controller.md.sal.common.impl.ListenerRegistry
 
 public class BrokerImpl implements Broker {
     private static val log = LoggerFactory.getLogger(BrokerImpl);
@@ -42,17 +41,14 @@ public class BrokerImpl implements Broker {
     private val Map<Class<? extends BrokerService>, BrokerModule> serviceProviders = Collections.
         synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerModule>());
 
-
-    private val rpcRegistrationListeners = new ListenerRegistry<RpcRegistrationListener>();
-    // RPC Context
-    private val Map<QName, RpcImplementation> rpcImpls = Collections.synchronizedMap(
-        new HashMap<QName, RpcImplementation>());
-
     // Implementation specific
     @Property
     private var ExecutorService executor = Executors.newFixedThreadPool(5);
     @Property
     private var BundleContext bundleContext;
+    
+    @Property
+    private var RpcRouter router;
 
     override registerConsumer(Consumer consumer, BundleContext ctx) {
         checkPredicates(consumer);
@@ -95,42 +91,8 @@ public class BrokerImpl implements Broker {
         return prov.getServiceForSession(service, session);
     }
 
-    // RPC Functionality
-    protected def void addRpcImplementation(QName rpcType, RpcImplementation implementation) {
-        if(rpcImpls.get(rpcType) != null) {
-            throw new IllegalStateException("Implementation for rpc " + rpcType + " is already registered.");
-        }
-
-        
-        rpcImpls.put(rpcType, implementation);
-
-        
-        for(listener : rpcRegistrationListeners.listeners)  {
-            try {
-                listener.instance.onRpcImplementationAdded(rpcType);
-            } catch (Exception e){
-                log.error("Unhandled exception during invoking listener",e);
-            }
-        }
-    }
-
-    protected def void removeRpcImplementation(QName rpcType, RpcImplementation implToRemove) {
-        if(implToRemove == rpcImpls.get(rpcType)) {
-            rpcImpls.remove(rpcType);
-        }
-        
-        for(listener : rpcRegistrationListeners.listeners)  {
-            try {
-                listener.instance.onRpcImplementationRemoved(rpcType);
-            } catch (Exception e){
-                log.error("Unhandled exception during invoking listener",e);
-            }
-        }
-    }
-
     protected def Future<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
-        val impl = rpcImpls.get(rpc);
-        val result = executor.submit([|impl.invokeRpc(rpc, input)] as Callable<RpcResult<CompositeNode>>);
+        val result = executor.submit([|router.invokeRpc(rpc, input)] as Callable<RpcResult<CompositeNode>>);
         return result;
     }
 
@@ -171,12 +133,4 @@ public class BrokerImpl implements Broker {
         sessions.remove(consumerContextImpl);
         providerSessions.remove(consumerContextImpl);
     }
-    
-    protected def getSupportedRpcs() {
-        rpcImpls.keySet;
-    }
-    
-    def ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener) {
-        rpcRegistrationListeners.register(listener);
-    }
 }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java
new file mode 100644 (file)
index 0000000..7037b46
--- /dev/null
@@ -0,0 +1,118 @@
+package org.opendaylight.controller.sal.dom.broker;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+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.notify.NotificationListener;
+import org.opendaylight.controller.sal.dom.broker.impl.DataReaderRouter;
+import org.opendaylight.controller.sal.dom.broker.impl.NotificationRouterImpl;
+import org.opendaylight.controller.sal.dom.broker.impl.RpcRouterImpl;
+import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
+import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+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;
+
+public class MountPointImpl implements MountProvisionInstance {
+
+    final RpcRouter rpcs;
+    final DataReaderRouter dataReader;
+    final NotificationRouter notificationRouter;
+
+    public MountPointImpl(InstanceIdentifier path) {
+        rpcs = new RpcRouterImpl("");
+        dataReader = new DataReaderRouter();
+        notificationRouter = new NotificationRouterImpl();
+    }
+
+    @Override
+    public void publish(CompositeNode notification) {
+        notificationRouter.publish(notification);
+    }
+
+    @Override
+    public Registration<NotificationListener> addNotificationListener(QName notification, NotificationListener listener) {
+        return notificationRouter.addNotificationListener(notification, listener);
+    }
+
+    @Override
+    public CompositeNode readConfigurationData(InstanceIdentifier path) {
+        return dataReader.readConfigurationData(path);
+    }
+
+    @Override
+    public CompositeNode readOperationalData(InstanceIdentifier path) {
+        return dataReader.readOperationalData(path);
+    }
+
+    public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerOperationalReader(
+            InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader) {
+        return dataReader.registerOperationalReader(path, reader);
+    }
+
+    public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerConfigurationReader(
+            InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader) {
+        return dataReader.registerConfigurationReader(path, reader);
+    }
+
+    @Override
+    public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+        return rpcs.addRoutedRpcImplementation(rpcType, implementation);
+    }
+
+    @Override
+    public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+            throws IllegalArgumentException {
+        return rpcs.addRpcImplementation(rpcType, implementation);
+    }
+
+    public Set<QName> getSupportedRpcs() {
+        return rpcs.getSupportedRpcs();
+    }
+
+    
+    public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+        return rpcs.invokeRpc(rpc, input);
+    }
+
+    public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener) {
+        return rpcs.addRpcRegistrationListener(listener);
+    }
+
+
+    @Override
+    public Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(InstanceIdentifier path,
+            DataChangeListener listener) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void sendNotification(CompositeNode notification) {
+        publish(notification);
+        
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend
new file mode 100644 (file)
index 0000000..c64d1e5
--- /dev/null
@@ -0,0 +1,35 @@
+package org.opendaylight.controller.sal.dom.broker
+
+
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import java.util.concurrent.ConcurrentMap
+import java.util.concurrent.ConcurrentHashMap
+import static com.google.common.base.Preconditions.*;
+
+class MountPointManagerImpl implements MountProvisionService {
+    
+    ConcurrentMap<InstanceIdentifier,MountPointImpl> mounts = new ConcurrentHashMap();
+    
+    override createMountPoint(InstanceIdentifier path) {
+        checkState(!mounts.containsKey(path),"Mount already created");
+        val mount = new MountPointImpl(path);
+        mounts.put(path,mount);
+    }
+    
+    
+    override createOrGetMountPoint(InstanceIdentifier path) {
+        val mount = mounts.get(path);
+        if(mount === null) {
+            return createMountPoint(path)
+        }
+        return mount;
+    }
+    
+    
+    override getMountPoint(InstanceIdentifier path) {
+        mounts.get(path);
+    }
+    
+    
+}
index bffc5705962b3fd7f2ad790a11214dee8166cbc7..cf5d220943142ad83c948299aeef3222d882b136 100644 (file)
@@ -1,25 +1,23 @@
 package org.opendaylight.controller.sal.dom.broker
 
-import java.util.Collections
-import java.util.HashMap
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession
 import org.opendaylight.controller.sal.core.api.Provider
 import org.opendaylight.controller.sal.core.api.RpcImplementation
 import org.opendaylight.yangtools.yang.common.QName
 import org.osgi.framework.BundleContext
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration
 import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration
-import static java.util.Collections.*
-import java.util.Collections
-import java.util.HashMap
 import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
+import org.opendaylight.yangtools.concepts.Registration
+
+import java.util.Set
+import java.util.HashSet
 
 class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession {
 
     @Property
     private val Provider provider;
 
-    private val rpcImpls = Collections.synchronizedMap(new HashMap<QName, RpcImplementation>());
+    private val Set<Registration<?>> registrations = new HashSet();
 
     new(Provider provider, BundleContext ctx) {
         super(null, ctx);
@@ -27,39 +25,22 @@ class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession
     }
 
     override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
-        if (rpcType == null) {
-            throw new IllegalArgumentException("rpcType must not be null");
-        }
-        if (implementation == null) {
-            throw new IllegalArgumentException("Implementation must not be null");
-        }
-        broker.addRpcImplementation(rpcType, implementation);
-        rpcImpls.put(rpcType, implementation);
-
-        return new RpcRegistrationImpl(rpcType, implementation, this);
+        val origReg = broker.router.addRpcImplementation(rpcType, implementation);
+        val newReg = new RpcRegistrationWrapper(origReg);
+        registrations.add(newReg);
+        return newReg;
     }
 
-    def removeRpcImplementation(RpcRegistrationImpl implToRemove) throws IllegalArgumentException {
-        val localImpl = rpcImpls.get(implToRemove.type);
-        if (localImpl !== implToRemove.instance) {
-            throw new IllegalStateException("Implementation was not registered in this session");
-        }
-        broker.removeRpcImplementation(implToRemove.type, localImpl);
-        rpcImpls.remove(implToRemove.type);
+    protected def removeRpcImplementation(RpcRegistrationWrapper implToRemove) throws IllegalArgumentException {
+        registrations.remove(implToRemove);
     }
-
+    
     override close() {
-        removeAllRpcImlementations
-        super.close
-    }
-
-    private def removeAllRpcImlementations() {
-        if (!rpcImpls.empty) {
-            for (entry : rpcImpls.entrySet) {
-                broker.removeRpcImplementation(entry.key, entry.value);
-            }
-            rpcImpls.clear
+        
+        for (reg : registrations) {
+            reg.close()
         }
+        super.close
     }
 
     override addMountedRpcImplementation(QName rpcType, RpcImplementation implementation) {
@@ -71,30 +52,34 @@ class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession
     }
 
     override getSupportedRpcs() {
-        broker.getSupportedRpcs();
+        broker.router.supportedRpcs;
     }
 
     override addRpcRegistrationListener(RpcRegistrationListener listener) {
-        broker.addRpcRegistrationListener(listener);
+        broker.router.addRpcRegistrationListener(listener);
     }
 }
 
-class RpcRegistrationImpl extends AbstractObjectRegistration<RpcImplementation> implements RpcRegistration {
+class RpcRegistrationWrapper implements RpcRegistration {
+
 
     @Property
-    val QName type
+    val RpcRegistration delegate
 
-    private var ProviderContextImpl context
+    new(RpcRegistration delegate) {
+        _delegate = delegate
+    }
 
-    new(QName type, RpcImplementation instance, ProviderContextImpl ctx) {
-        super(instance)
-        _type = type
-        context = ctx
+    override getInstance() {
+        delegate.instance
     }
 
-    override protected removeRegistration() {
-        context.removeRpcImplementation(this)
-        context = null
+    override close() {
+        delegate.close
     }
 
+    override getType() {
+        delegate.type
+    }
 }
+
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend
new file mode 100644 (file)
index 0000000..1e0f338
--- /dev/null
@@ -0,0 +1,13 @@
+package org.opendaylight.controller.sal.dom.broker.impl
+
+import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+
+class DataReaderRouter extends AbstractDataReadRouter<InstanceIdentifier, CompositeNode> {
+
+    override protected merge(InstanceIdentifier path, Iterable<CompositeNode> data) {
+        return data.iterator.next
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java
new file mode 100644 (file)
index 0000000..6d7b600
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.dom.broker.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;
+import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationService;
+import org.opendaylight.controller.sal.core.spi.BrokerModule;
+import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+public class NotificationRouterImpl implements NotificationRouter {
+    private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class);
+
+    private Multimap<QName, Registration<NotificationListener>> listeners = HashMultimap.create();
+
+    private void sendNotification(CompositeNode notification) {
+        QName type = notification.getNodeType();
+        Collection<Registration<NotificationListener>> toNotify = listeners.get(type);
+        log.info("Publishing notification " + type);
+
+        if (toNotify == null) {
+            // No listeners were registered - returns.
+            return;
+        }
+
+        for (Registration<NotificationListener> listener : toNotify) {
+            try {
+                // FIXME: ensure that notification is immutable
+                listener.getInstance().onNotification(notification);
+            } catch (Exception e) {
+                log.error("Uncaught exception in NotificationListener", e);
+            }
+        }
+
+    }
+
+    @Override
+    public void publish(CompositeNode notification) {
+        sendNotification(notification);
+    }
+
+    @Override
+    public Registration<NotificationListener> addNotificationListener(QName notification, NotificationListener listener) {
+        ListenerRegistration ret = new ListenerRegistration(notification, listener);
+        return ret;
+    }
+
+    private class ListenerRegistration extends AbstractObjectRegistration<NotificationListener> {
+
+        final QName type;
+
+        public ListenerRegistration(QName type, NotificationListener instance) {
+            super(instance);
+            this.type = type;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            listeners.remove(type, this);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend
new file mode 100644 (file)
index 0000000..d67697f
--- /dev/null
@@ -0,0 +1,106 @@
+package org.opendaylight.controller.sal.dom.broker.impl
+
+import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
+import org.opendaylight.yangtools.concepts.Identifiable
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.controller.sal.core.api.RpcImplementation
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import static com.google.common.base.Preconditions.*;
+import java.util.Map
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration
+import java.util.concurrent.ConcurrentHashMap
+import java.util.Set
+import java.util.Collections
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration
+import org.opendaylight.controller.md.sal.common.impl.ListenerRegistry
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
+import org.slf4j.LoggerFactory
+
+class RpcRouterImpl implements RpcRouter, Identifiable<String> {
+
+    static val log = LoggerFactory.getLogger(RpcRouterImpl)
+
+    Map<QName, RpcRegistration> implementations = new ConcurrentHashMap();
+
+    @Property
+    val Set<QName> supportedRpcs = Collections.unmodifiableSet(implementations.keySet);
+
+    private val rpcRegistrationListeners = new ListenerRegistry<RpcRegistrationListener>();
+
+    @Property
+    val String identifier;
+
+    new(String name) {
+        _identifier = name;
+    }
+
+    override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+    }
+
+    override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
+        checkNotNull(rpcType, "Rpc Type should not be null");
+        checkNotNull(implementation, "Implementation should not be null.");
+        checkState(!implementations.containsKey(rpcType), "Provider for supplied rpc is already registered.");
+        val reg = new RpcRegistrationImpl(rpcType, implementation, this);
+        implementations.put(rpcType, reg)
+
+        for (listener : rpcRegistrationListeners.listeners) {
+            try {
+                listener.instance.onRpcImplementationAdded(rpcType);
+            } catch (Exception e) {
+                log.error("Unhandled exception during invoking listener", e);
+            }
+        }
+
+        return reg;
+
+    }
+
+    override invokeRpc(QName rpc, CompositeNode input) {
+        checkNotNull(rpc, "Rpc Type should not be null");
+
+        val impl = implementations.get(rpc);
+        checkState(impl !== null, "Provider for supplied rpc is not registered.");
+
+        return impl.instance.invokeRpc(rpc, input);
+    }
+
+    def remove(RpcRegistrationImpl impl) {
+        val existing = implementations.get(impl.type);
+        if (existing == impl) {
+            implementations.remove(impl.type);
+        }
+        for (listener : rpcRegistrationListeners.listeners) {
+            try {
+                listener.instance.onRpcImplementationRemoved(impl.type);
+            } catch (Exception e) {
+                log.error("Unhandled exception during invoking listener", e);
+            }
+        }
+    }
+    
+    override addRpcRegistrationListener(RpcRegistrationListener listener) {
+        rpcRegistrationListeners.register(listener);
+    }
+
+}
+
+class RpcRegistrationImpl extends AbstractObjectRegistration<RpcImplementation> implements RpcRegistration {
+
+    @Property
+    val QName type;
+
+    @Property
+    var RpcRouterImpl router;
+
+    new(QName type, RpcImplementation instance, RpcRouterImpl router) {
+        super(instance)
+        _type = type
+        _router = router
+    }
+
+    override protected removeRegistration() {
+        router.remove(this);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/NotificationRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/NotificationRouter.java
new file mode 100644 (file)
index 0000000..fc42d2c
--- /dev/null
@@ -0,0 +1,21 @@
+package org.opendaylight.controller.sal.dom.broker.spi;
+
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public interface NotificationRouter {
+
+    void publish(CompositeNode notification);
+
+    /**
+     * Registers a notification listener for supplied notification type.
+     * 
+     * @param notification
+     * @param listener
+     */
+    Registration<NotificationListener> addNotificationListener(QName notification,
+            NotificationListener listener);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RoutedRpcProcessor.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RoutedRpcProcessor.java
new file mode 100644 (file)
index 0000000..97c2a15
--- /dev/null
@@ -0,0 +1,27 @@
+package org.opendaylight.controller.sal.dom.broker.spi;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+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.InstanceIdentifier;
+
+public interface RoutedRpcProcessor extends RpcImplementation {
+
+    public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation);
+
+    public Set<QName> getSupportedRpcs();
+
+    public QName getRpcType();
+    
+    public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input);
+
+    Map<InstanceIdentifier,RpcImplementation> getRoutes();
+    
+    RpcImplementation getDefaultRoute();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RpcRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/spi/RpcRouter.java
new file mode 100644 (file)
index 0000000..6886f89
--- /dev/null
@@ -0,0 +1,31 @@
+package org.opendaylight.controller.sal.dom.broker.spi;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public interface RpcRouter extends RpcProvisionRegistry, RpcImplementation {
+
+    @Override
+    public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation);
+    
+    @Override
+    public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+            throws IllegalArgumentException;
+    
+    @Override
+    public Set<QName> getSupportedRpcs();
+    
+    @Override
+    public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input);
+
+    ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
+}
index c8bc71577dea0014e3dd248b88d571b00c399c21..57e4d858c196beee2c9bf5a5d6ac04a7ed0d6d78 100644 (file)
@@ -6,6 +6,9 @@
         <artifactId>sal-parent</artifactId>
         <version>1.0-SNAPSHOT</version>
     </parent>
+    <properties>
+        <netconf.version>0.2.2-SNAPSHOT</netconf.version>
+    </properties>
     <artifactId>sal-netconf-connector</artifactId>
     <scm>
         <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
             <groupId>${project.groupId}</groupId>
             <artifactId>sal-connector-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sal-common-util</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.xtend</groupId>
+            <artifactId>org.eclipse.xtend.lib</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>netconf-client</artifactId>
+            <version>0.2.2-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+            <version>0.5.9-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test</artifactId>
+            <version>${netconf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>${netconf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>${netconf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-api</artifactId>
+            <version>${netconf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${netconf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>util</artifactId>
+            <scope>test</scope>
+            <version>0.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-client</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-netconf-connector</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-persister-impl</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-impl</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-impl</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-impl</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>logback-config</artifactId>
+            <scope>test</scope>
+            <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
     </dependencies>
 
     <packaging>bundle</packaging>
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 38fef96a8c72f0292fab194240a53c4008550a35..f5605eaffe1510ecc60f1ac58327b182e11c0035 100644 (file)
               org.opendaylight.controller.forwardingrulesmanager,
               org.opendaylight.controller.sal.authorization,
               org.opendaylight.controller.sal.action,
+              org.opendaylight.controller.sal.connection,
               org.opendaylight.controller.sal.core,
               org.opendaylight.controller.sal.utils,
               org.opendaylight.controller.containermanager,
+              org.opendaylight.controller.connectionmanager,
               org.opendaylight.controller.switchmanager,
               org.opendaylight.controller.forwarding.staticrouting,
               org.opendaylight.controller.usermanager,
       <artifactId>forwardingrulesmanager</artifactId>
       <version>0.4.1-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>connectionmanager</artifactId>
+      <version>0.1.1-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
index 6bde9d7c9311c1b9b2340036dec46cf6d8d35c9b..3d33edcbf2272c308cdb2236c574aaa42cd95ed9 100644 (file)
@@ -12,6 +12,7 @@ import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -23,10 +24,12 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.codehaus.jackson.map.ObjectMapper;
+import org.opendaylight.controller.connectionmanager.IConnectionManager;
 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
 import org.opendaylight.controller.sal.authorization.Privilege;
 import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
 import org.opendaylight.controller.sal.core.Config;
 import org.opendaylight.controller.sal.core.Description;
 import org.opendaylight.controller.sal.core.ForwardingMode;
@@ -38,8 +41,10 @@ import org.opendaylight.controller.sal.core.State;
 import org.opendaylight.controller.sal.core.Tier;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
 import org.opendaylight.controller.sal.utils.TierHelper;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.switchmanager.SpanConfig;
@@ -49,6 +54,7 @@ import org.opendaylight.controller.switchmanager.SwitchConfig;
 import org.opendaylight.controller.web.DaylightWebUtil;
 import org.opendaylight.controller.web.IDaylightWeb;
 import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -729,6 +735,100 @@ public class Devices implements IDaylightWeb {
         return resultBean;
     }
 
+    @RequestMapping(value = "/connect/nodes", method = RequestMethod.GET)
+    @ResponseBody
+    public List<NodeJsonBean> getNodes(HttpServletRequest request) {
+        IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance(
+                IConnectionManager.class, this);
+        if (connectionManager == null) {
+            return null;
+        }
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class,
+                GlobalConstants.DEFAULT.toString(), this);
+        if (switchManager == null) {
+            return null;
+        }
+
+        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 = node.toString();
+            if (descriptionProperty != null) {
+                description = descriptionProperty.getValue();
+            }
+            NodeJsonBean nodeBean = new NodeJsonBean();
+            nodeBean.setNodeId(node.getNodeIDString());
+            nodeBean.setNodeType(node.getType());
+            if (description.equals("None")) {
+                nodeBean.setNodeName(node.toString());
+            } else {
+                nodeBean.setNodeName(description);
+            }
+            result.add(nodeBean);
+        }
+
+        return result;
+    }
+
+    @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST)
+    @ResponseBody
+    public Status addNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
+            @RequestParam(required = true) String ipAddress, @RequestParam(required = true) String port,
+            @RequestParam(required = false) String nodeType) {
+        IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance(
+                IConnectionManager.class, this);
+        if (connectionManager == null) {
+            return new Status(StatusCode.NOTFOUND, "Service not found");
+        }
+
+        if (!NetUtils.isIPv4AddressValid(ipAddress)) {
+            return new Status(StatusCode.NOTACCEPTABLE, "Invalid IP Address: " + ipAddress);
+        }
+
+        try {
+            Integer.parseInt(port);
+        } catch (Exception e) {
+            return new Status(StatusCode.NOTACCEPTABLE, "Invalid Layer 4 Port: " + port);
+        }
+
+        Map<ConnectionConstants, String> params = new HashMap<ConnectionConstants, String>();
+        params.put(ConnectionConstants.ADDRESS, ipAddress);
+        params.put(ConnectionConstants.PORT, port);
+
+        Node node = null;
+        if (nodeType != null) {
+            node = connectionManager.connect(nodeType, nodeId, params);
+        } else {
+            node = connectionManager.connect(nodeId, params);
+        }
+        if (node == null) {
+            return new Status(StatusCode.NOTFOUND, "Failed to connect to Node at " + ipAddress + ":" + port);
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST)
+    @ResponseBody
+    public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
+            @RequestParam(required = true) String nodeType) {
+        IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance(
+                IConnectionManager.class, this);
+        if (connectionManager == null) {
+            return new Status(StatusCode.NOTFOUND, "Service not found");
+        }
+
+        try {
+            Node node = new Node(nodeType, nodeId);
+            return connectionManager.disconnect(node);
+        } catch (Exception e) {
+            return new Status(StatusCode.NOTFOUND, "Resource not found");
+        }
+    }
+
     private String getNodeDesc(String nodeId, String containerName) {
         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
                 this);
@@ -766,4 +866,4 @@ public class Devices implements IDaylightWeb {
         return "forward:" + "/";
     }
 
-}
\ No newline at end of file
+}
index 61d4f2f91a6e322abf937198436d0d13596afe19..e5f2b6a2c0ece9c671a21bbe87d4ada97a61f973 100644 (file)
@@ -13,6 +13,7 @@ import java.util.List;
 public class NodeJsonBean {
     String nodeId;
     String nodeName;
+    String nodeType;
     List<PortJsonBean> nodePorts;
 
     public String getNodeId() {
@@ -23,6 +24,10 @@ public class NodeJsonBean {
         return nodeName;
     }
 
+    public String getNodeType() {
+        return nodeType;
+    }
+
     public List<PortJsonBean> getNodePorts() {
         return nodePorts;
     }
@@ -35,6 +40,10 @@ public class NodeJsonBean {
         this.nodeName = nodeName;
     }
 
+    public void setNodeType(String nodeType) {
+        this.nodeType = nodeType;
+    }
+
     public void setNodePorts(List<PortJsonBean> port) {
         this.nodePorts = port;
     }
index b02a15a7e5748f96368e425d2a374f04f1647b74..cc142bf9672f136686a952e88c5c99395f6c2d8e 100644 (file)
@@ -12,1577 +12,1796 @@ one.f = {};
 // specify dashlets and layouts
 
 one.f.dashlet = {
-    nodesLearnt : {
-        id : 'nodesLearnt',
-        name : 'Nodes Learned'
-    },
-    staticRouteConfig : {
-        id : 'staticRouteConfig',
-        name : 'Static Route Configuration'
-    },
-    subnetGatewayConfig : {
-        id : 'subnetGatewayConfig',
-        name : 'Subnet Gateway Configuration'
-    },
-    spanPortConfig : {
-        id : 'spanPortConfig',
-        name : 'SPAN Port Configuration'
-    }
+  nodesLearnt : {
+    id : 'nodesLearnt',
+    name : 'Nodes Learned'
+  },
+  staticRouteConfig : {
+    id : 'staticRouteConfig',
+    name : 'Static Route Configuration'
+  },
+  subnetGatewayConfig : {
+    id : 'subnetGatewayConfig',
+    name : 'Subnet Gateway Configuration'
+  },
+  spanPortConfig : {
+    id : 'spanPortConfig',
+    name : 'SPAN Port Configuration'
+  },
+  connection : {
+    id : 'connection',
+    name : 'Connection Manager'
+  }
 };
 
 one.f.menu = {
-    left : {
-        top : [
-            one.f.dashlet.nodesLearnt
-        ],
-        bottom : [
-            one.f.dashlet.staticRouteConfig
-        ]
-    },
-    right : {
-        top : [],
-        bottom : [
-            one.f.dashlet.subnetGatewayConfig,
-            one.f.dashlet.spanPortConfig
-        ]
-    }
+  left : {
+    top : [
+      one.f.dashlet.nodesLearnt,
+      one.f.dashlet.connection
+      ],
+    bottom : [
+      one.f.dashlet.staticRouteConfig
+      ]
+  },
+  right : {
+    top : [],
+    bottom : [
+      one.f.dashlet.subnetGatewayConfig,
+    one.f.dashlet.spanPortConfig
+      ]
+  }
 };
 
 /**Devices Modules */
 one.f.switchmanager = {
-    rootUrl: "controller/web/devices",
-    createTable: function(columnNames, body) {
-        var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
-        var $table = one.lib.dashlet.table.table(tableAttributes);
-        var tableHeaders = columnNames;
-        var $thead = one.lib.dashlet.table.header(tableHeaders);
-        var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
-        $table.append($thead)
-        .append($tbody);
-        return $table;
-    },
-    validateName: function(name) {
-        return (name.length < 256);
-    }
+  rootUrl: "controller/web/devices",
+  createTable: function(columnNames, body) {
+    var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
+    var $table = one.lib.dashlet.table.table(tableAttributes);
+    var tableHeaders = columnNames;
+    var $thead = one.lib.dashlet.table.header(tableHeaders);
+    var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
+    $table.append($thead)
+      .append($tbody);
+    return $table;
+  },
+  validateName: function(name) {
+    return (name.length < 256);
+  }
 };
 
 one.f.switchmanager.nodesLearnt = {
-    id: {
-        dashlet: {
-            popout: "one_f_switchmanager_nodesLearnt_id_dashlet_popout",
-            datagrid: "one_f_switchmanager_nodesLearnt_id_dashlet_datagrid"
-        },
-        modal: {
-            modal: "one_f_switchmanager_nodesLearnt_id_modal_modal",
-            configure: "one_f_switchmanager_nodesLearnt_id_modal_configure",
-            ports: "one_f_switchmanager_nodesLearnt_id_modal_ports",
-            save: "one_f_switchmanager_nodesLearnt_id_modal_save",
-            datagrid: "one_f_switchmanager_nodesLearnt_id_modal_datagrid",
-            portsDatagrid: "one_f_switchmanager_nodesLearnt_id_modal_portsDatagrid",
-            form: {
-                nodeId: "one_f_switchmanager_nodesLearnt_id_modal_form_nodeid",
-                nodeName : "one_f_switchmanager_nodesLearnt_id_modal_form_nodename",
-                portStatus : "one_f_switchmanager_nodesLearnt_id_modal_form_portstatus",
-                tier: "one_f_switchmanager_nodesLearnt_id_modal_form_tier",
-                operationMode: "one_f_switchmanager_nodesLearnt_id_modal_form_opmode"
-            }
-        }
+  id: {
+    dashlet: {
+      popout: "one_f_switchmanager_nodesLearnt_id_dashlet_popout",
+      datagrid: "one_f_switchmanager_nodesLearnt_id_dashlet_datagrid"
     },
-    dashlet: function($dashlet) {
-        var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
-        one.lib.dashlet.empty($dashlet);
-        $dashlet.append(one.lib.dashlet.header(one.f.dashlet.nodesLearnt.name));
-
-        one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.abridged(content);
-            $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
-                $(this).find("tbody a").click(one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
-            });
-            
-            $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
-                $(this).find("tbody span").click(function(){
-                    one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
-                });
-            });
-            
+    modal: {
+      modal: "one_f_switchmanager_nodesLearnt_id_modal_modal",
+      configure: "one_f_switchmanager_nodesLearnt_id_modal_configure",
+      ports: "one_f_switchmanager_nodesLearnt_id_modal_ports",
+      save: "one_f_switchmanager_nodesLearnt_id_modal_save",
+      datagrid: "one_f_switchmanager_nodesLearnt_id_modal_datagrid",
+      portsDatagrid: "one_f_switchmanager_nodesLearnt_id_modal_portsDatagrid",
+      form: {
+        nodeId: "one_f_switchmanager_nodesLearnt_id_modal_form_nodeid",
+        nodeName : "one_f_switchmanager_nodesLearnt_id_modal_form_nodename",
+        portStatus : "one_f_switchmanager_nodesLearnt_id_modal_form_portstatus",
+        tier: "one_f_switchmanager_nodesLearnt_id_modal_form_tier",
+        operationMode: "one_f_switchmanager_nodesLearnt_id_modal_form_opmode"
+      }
+    }
+  },
+  dashlet: function($dashlet) {
+    var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
+    one.lib.dashlet.empty($dashlet);
+    $dashlet.append(one.lib.dashlet.header(one.f.dashlet.nodesLearnt.name));
+
+    one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.abridged(content);
+      $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+        $(this).find("tbody a").click(one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
+      });
+
+      $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+        $(this).find("tbody span").click(function(){
+          one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
         });
+      });
+
+    });
+  },
+  ajax : {
+    main : function(url, callback) {
+      $.getJSON(url, function(data) {
+        callback(data);
+      });
+    }
+  },
+  modal : {
+    initialize: {
+      updateNode: function(evt) {
+        one.f.switchmanager.nodesLearnt.ajax.main(one.f.switchmanager.rootUrl + "/tiers", function(tiers) {
+
+          var nodeId = decodeURIComponent(evt.target.id);
+          var h3;
+          var footer = [];
+          var $body = one.f.switchmanager.nodesLearnt.modal.body.updateNode(nodeId, JSON.parse(decodeURIComponent(evt.target.getAttribute("switchDetails"))), tiers);
+          if (evt.target.getAttribute("privilege") == 'WRITE'){
+            h3 = "Update Node Information";
+            footer = one.f.switchmanager.nodesLearnt.modal.footer.updateNode();
+          } else { //disable node edit
+            $body.find('*').attr('disabled', 'disabled');
+            h3 = 'Node Information';
+          }
+
+          var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.configure, h3, "", footer);
+          // bind save button
+          $('#' + one.f.switchmanager.nodesLearnt.id.modal.save, $modal).click(function() {
+            one.f.switchmanager.nodesLearnt.modal.save($modal);
+          });
+
+          // inject body (nodePorts)
+          one.lib.modal.inject.body($modal, $body);
+          $modal.modal();
+        });
+      },
+      popout: function() {
+        var h3 = "Nodes Learned";
+        var footer = one.f.switchmanager.nodesLearnt.modal.footer.popout();
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.modal, h3, "", footer);
+        var $body = one.f.switchmanager.nodesLearnt.modal.body.popout($modal);
+        return $modal;
+      },
+      displayPorts: function(ports) {
+        var content = JSON.parse(decodeURIComponent(ports.attr("ports")));
+
+        var h3 = ((ports.attr("nodeName") == "None")? ports.attr("nodeId") : ports.attr("nodeName"))
+          var footer = [];
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.ports, h3, "", footer);
+
+        var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid, {
+          searchable: true,
+            filterable: false,
+            pagination: true,
+            flexibleRowsPerPage: true,
+            popout: true
+        }, "table-striped table-condensed");
+        one.lib.modal.inject.body($modal, $gridHTML);
+        $modal.on("shown", function() {
+          var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.displayPorts(content);
+          $("#" + one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid).datagrid({
+            dataSource: dataSource,
+            stretchHeight: false
+          });
+        });
+        $modal.modal();
+      }
     },
-    ajax : {
-        main : function(url, callback) {
-            $.getJSON(url, function(data) {
-                callback(data);
-            });
+    body: {
+      updateNode: function(nodeId, switchDetails, tiers) {
+        var $form = $(document.createElement('form'));
+        var $fieldset = $(document.createElement('fieldset'));
+        // node ID. not editable.
+        var $label = one.lib.form.label("Node ID");
+        var $input = one.lib.form.input("node id");
+        $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeId);
+        $input.attr("disabled", true);
+        $input.attr("value", nodeId);
+        $fieldset.append($label).append($input);
+        // node name
+        var $label = one.lib.form.label("Node Name");
+        var $input = one.lib.form.input("Node Name");
+        $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeName);
+        if(switchDetails["nodeName"] != null) {
+          $input.attr('value', switchDetails["nodeName"]);
+        }
+        $fieldset.append($label).append($input);
+        // node tier
+        var $label = one.lib.form.label("Tier");
+        var $select = one.lib.form.select.create(tiers);
+        $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.tier);
+        $select.val(switchDetails["tier"]);
+        $fieldset.append($label).append($select);
+        // operation mode
+        var $label = one.lib.form.label("Operation Mode");
+        var $select = one.lib.form.select.create(
+            ["Allow reactive forwarding", "Proactive forwarding only"]);
+        $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.operationMode);
+        if ((one.main.registry != undefined) && (one.main.registry.container != 'default')) {
+          $select.attr("disabled", true);
         }
+        $select.val(switchDetails["mode"]);
+        $fieldset.append($label).append($select);
+        $form.append($fieldset);
+        return $form;
+      },
+      popout: function($modal) {
+        var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.datagrid, {
+          searchable: true,
+        filterable: false,
+        pagination: true,
+        flexibleRowsPerPage: true,
+        popout: true
+        }, "table-striped table-condensed");
+        one.lib.modal.inject.body($modal, $gridHTML);
+        // attach to shown event of modal 
+        $modal.on("shown", function() {
+          var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
+          one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
+            var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.popout(content);
+            $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).datagrid({
+              dataSource: dataSource,
+              stretchHeight: false
+            })
+            .on("loaded", function() {
+              $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).find("tbody span").click(function(){
+                one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
+              });
+            });
+          });
+        });
+      }
     },
-    modal : {
-        initialize: {
-            updateNode: function(evt) {
-                one.f.switchmanager.nodesLearnt.ajax.main(one.f.switchmanager.rootUrl + "/tiers", function(tiers) {
-
-                    var nodeId = decodeURIComponent(evt.target.id);
-                    var h3;
-                    var footer = [];
-                    var $body = one.f.switchmanager.nodesLearnt.modal.body.updateNode(nodeId, JSON.parse(decodeURIComponent(evt.target.getAttribute("switchDetails"))), tiers);
-                    if (evt.target.getAttribute("privilege") == 'WRITE'){
-                        h3 = "Update Node Information";
-                        footer = one.f.switchmanager.nodesLearnt.modal.footer.updateNode();
-                    } else { //disable node edit
-                        $body.find('*').attr('disabled', 'disabled');
-                        h3 = 'Node Information';
-                    }
-                    
-                    var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.configure, h3, "", footer);
-                    // bind save button
-                    $('#' + one.f.switchmanager.nodesLearnt.id.modal.save, $modal).click(function() {
-                        one.f.switchmanager.nodesLearnt.modal.save($modal);
-                    });
-
-                    // inject body (nodePorts)
-                    one.lib.modal.inject.body($modal, $body);
-                    $modal.modal();
-                });
-            },
-            popout: function() {
-                var h3 = "Nodes Learned";
-                var footer = one.f.switchmanager.nodesLearnt.modal.footer.popout();
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.modal, h3, "", footer);
-                var $body = one.f.switchmanager.nodesLearnt.modal.body.popout($modal);
-                return $modal;
-            },
-            displayPorts: function(ports) {
-                var content = JSON.parse(decodeURIComponent(ports.attr("ports")));
-                
-                var h3 = ((ports.attr("nodeName") == "None")? ports.attr("nodeId") : ports.attr("nodeName"))
-                var footer = [];
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.ports, h3, "", footer);
-                
-                var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid, {
-                    searchable: true,
-                    filterable: false,
-                    pagination: true,
-                    flexibleRowsPerPage: true,
-                    popout: true
-                    }, "table-striped table-condensed");
-                one.lib.modal.inject.body($modal, $gridHTML);
-                $modal.on("shown", function() {
-                    var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.displayPorts(content);
-                    $("#" + one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid).datagrid({
-                        dataSource: dataSource,
-                        stretchHeight: false
-                    });
-                });
-                $modal.modal();
+    save: function($modal) {
+      var result = {};
+      result['nodeName'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeName, $modal).val();
+      if(!one.f.switchmanager.validateName(result['nodeName'])) {
+        alert("Node name can contain upto 255 characters");
+        return;
+      }
+      result['nodeId'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeId, $modal).val();
+      result['tier'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.tier, $modal).val();
+      result['operationMode'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.operationMode, $modal).val();
+      one.f.switchmanager.nodesLearnt.modal.ajax(result, 
+          function(response) {
+            if(response.status == true) {
+              $modal.modal('hide');
+              one.topology.update(); // refresh visual topology with new name
+              // TODO: Identify dashlet by inserting a nodesLearnt div 
+              // in the dashlet() instead
+              one.f.switchmanager.nodesLearnt.dashlet($("#left-top .dashlet"));
+            } else {
+              alert(response.message);
             }
+
+          });
+    },
+    ajax: function(requestData, callback) {
+      $.getJSON(one.f.switchmanager.rootUrl + "/nodesLearnt/update", requestData, function(response) {
+        callback(response);
+      });
+    },
+    footer: {
+      updateNode: function() {
+        var footer = [];
+        var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.nodesLearnt.id.modal.save, "btn-primary", "");
+        var $saveButton = one.lib.dashlet.button.button(saveButton);
+        footer.push($saveButton);
+
+        return footer;
+      },
+      popout: function() {
+        // TODO: Maybe put a close button in the footer?
+        return [];
+      }
+    }
+  },
+  // data functions
+  data : {
+    gridDataSource: {
+      abridged: function(data) {
+        var source = new StaticDataSource({
+          columns: [
+        {
+          property: 'nodeName',
+        label: 'Node Name',
+        sortable: true
         },
-        body: {
-            updateNode: function(nodeId, switchDetails, tiers) {
-                var $form = $(document.createElement('form'));
-                var $fieldset = $(document.createElement('fieldset'));
-                // node ID. not editable.
-                var $label = one.lib.form.label("Node ID");
-                var $input = one.lib.form.input("node id");
-                $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeId);
-                $input.attr("disabled", true);
-                $input.attr("value", nodeId);
-                $fieldset.append($label).append($input);
-                // node name
-                var $label = one.lib.form.label("Node Name");
-                var $input = one.lib.form.input("Node Name");
-                $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeName);
-                if(switchDetails["nodeName"] != null) {
-                    $input.attr('value', switchDetails["nodeName"]);
-                }
-                $fieldset.append($label).append($input);
-                // node tier
-                var $label = one.lib.form.label("Tier");
-                var $select = one.lib.form.select.create(tiers);
-                $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.tier);
-                $select.val(switchDetails["tier"]);
-                $fieldset.append($label).append($select);
-                // operation mode
-                var $label = one.lib.form.label("Operation Mode");
-                var $select = one.lib.form.select.create(
-                        ["Allow reactive forwarding", "Proactive forwarding only"]);
-                $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.operationMode);
-                if ((one.main.registry != undefined) && (one.main.registry.container != 'default')) {
-                    $select.attr("disabled", true);
-                }
-                $select.val(switchDetails["mode"]);
-                $fieldset.append($label).append($select);
-                $form.append($fieldset);
-                return $form;
-            },
-            popout: function($modal) {
-                var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.datagrid, {
-                        searchable: true,
-                        filterable: false,
-                        pagination: true,
-                        flexibleRowsPerPage: true,
-                        popout: true
-                        }, "table-striped table-condensed");
-                one.lib.modal.inject.body($modal, $gridHTML);
-                // attach to shown event of modal 
-                $modal.on("shown", function() {
-                    var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
-                    one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
-                        var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.popout(content);
-                        $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).datagrid({
-                            dataSource: dataSource,
-                            stretchHeight: false
-                        })
-                        .on("loaded", function() {
-                            $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).find("tbody span").click(function(){
-                                one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
-                            });
-                        });
-                    });
-                });
-            }
+        {
+          property: 'nodeId',
+        label: 'Node ID',
+        sortable: true
         },
-        save: function($modal) {
-            var result = {};
-            result['nodeName'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeName, $modal).val();
-            if(!one.f.switchmanager.validateName(result['nodeName'])) {
-                alert("Node name can contain upto 255 characters");
-                return;
-            }
-            result['nodeId'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeId, $modal).val();
-            result['tier'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.tier, $modal).val();
-            result['operationMode'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.operationMode, $modal).val();
-            one.f.switchmanager.nodesLearnt.modal.ajax(result, 
-                function(response) {
-                    if(response.status == true) {
-                        $modal.modal('hide');
-                        one.topology.update(); // refresh visual topology with new name
-                        // TODO: Identify dashlet by inserting a nodesLearnt div 
-                        // in the dashlet() instead
-                        one.f.switchmanager.nodesLearnt.dashlet($("#left-top .dashlet"));
-                    } else {
-                        alert(response.message);
-                    }
-                    
-                });
+        {
+          property: 'ports',
+        label: 'Ports',
+        sortable: true
+        }
+        ],
+        data: data.nodeData,
+        formatter: function(items) {
+          $.each(items, function (index, item) {
+            var nodeName = item.nodeName;
+            var nodeNameEntry = item.nodeName ? item.nodeName : "Click to update";
+            item.nodeName = '<a href="#" id="' + item.nodeId + '" switchDetails=' + encodeURIComponent(JSON.stringify(item)) + 
+            ' privilege=' + data.privilege + '>' + nodeNameEntry + '</a>';
+
+          var ports = item.ports;
+          var portsMatch = ports.match(/<\/span>/g);
+          var portsLength = 0;
+          if (portsMatch != null) {
+            portsLength = portsMatch.length;
+          }
+          item.ports = '<span class="nodePorts" style="cursor:pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId="' + item.nodeId
+            + '" nodeName="' + nodeName
+            + '">' + portsLength +'</span>';
+          }); 
         },
-        ajax: function(requestData, callback) {
-            $.getJSON(one.f.switchmanager.rootUrl + "/nodesLearnt/update", requestData, function(response) {
-                callback(response);
-            });
+        delay: 0
+        });
+        return source;
+
+      },
+      popout: function(data) {
+        var source = new StaticDataSource({
+          columns: [
+        {
+          property: 'nodeName',
+        label: 'Node Name',
+        sortable: true
         },
-        footer: {
-            updateNode: function() {
-                var footer = [];
-                var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.nodesLearnt.id.modal.save, "btn-primary", "");
-                var $saveButton = one.lib.dashlet.button.button(saveButton);
-                footer.push($saveButton);
-
-                return footer;
-            },
-            popout: function() {
-                // TODO: Maybe put a close button in the footer?
-                return [];
-            }
-        }
-    },
-    // data functions
-    data : {
-        gridDataSource: {
-            abridged: function(data) {
-                var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'nodeName',
-                            label: 'Node Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'nodeId',
-                            label: 'Node ID',
-                            sortable: true
-                        },
-                        {
-                            property: 'ports',
-                            label: 'Ports',
-                            sortable: true
-                        }
-                    ],
-                    data: data.nodeData,
-                    formatter: function(items) {
-                    $.each(items, function (index, item) {
-                        var nodeName = item.nodeName;
-                        var nodeNameEntry = item.nodeName ? item.nodeName : "Click to update";
-                        item.nodeName = '<a href="#" id="' + item.nodeId + '" switchDetails=' + encodeURIComponent(JSON.stringify(item)) + 
-                        ' privilege=' + data.privilege + '>' + nodeNameEntry + '</a>';
-                        
-                        var ports = item.ports;
-                        var portsMatch = ports.match(/<\/span>/g);
-                        var portsLength = 0;
-                        if (portsMatch != null) {
-                            portsLength = portsMatch.length;
-                        }
-                        item.ports = '<span class="nodePorts" style="cursor:pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId="' + item.nodeId
-                            + '" nodeName="' + nodeName
-                            + '">' + portsLength +'</span>';
-                    }); 
-                    },
-                    delay: 0
-                });
-                return source;
-
-            },
-            popout: function(data) {
-                var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'nodeName',
-                            label: 'Node Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'nodeId',
-                            label: 'Node ID',
-                            sortable: true
-                        },
-                        {
-                            property: 'tierName',
-                            label: 'Tier Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'mac',
-                            label: 'MAC Address',
-                            sortable: true
-                        },
-                        {
-                            property: 'ports',
-                            label: 'Ports',
-                            sortable: true
-                        }
-                    ],
-                    data: data.nodeData,
-                    formatter: function(items) {
-                        $.each(items, function (index, item) {
-                            var ports = item.ports;
-                            var portsMatch = ports.match(/<\/span>/g);
-                            var portsLength = 0;
-                            if (portsMatch != null) {
-                                portsLength = portsMatch.length;
-                            }
-                            item.ports = '<span class="nodePorts" style="cursor: pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId="' + item.nodeId
-                                + '" nodeName="' + item.nodeName
-                                + '">' + portsLength +'</span>';
-                        }); 
-                    },
-                    delay: 0
-                });
-                return source;
-            },
-            displayPorts: function(content){
-                var data=[];
-                var start=0;;
-                var finish=content.indexOf("<br>",start);
-                while(finish != -1){
-                    data.push({"ports":content.substring(start,finish)});
-                    start=finish+4
-                    finish=content.indexOf("<br>",start);
-                }
-                var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'ports',
-                            label: 'Ports',
-                            sortable: true
-                        }
-                    ],
-                    data:data,
-                    delay: 0
-                });
-                
-                return source;
-            }
+        {
+          property: 'nodeId',
+        label: 'Node ID',
+        sortable: true
         },
-        abridged : function(data) {
-            var result = [];
-            $.each(data.nodeData, function(key, value) {
-                var tr = {};
-                var entry = [];
-                var nodeNameEntry = value["nodeName"] ? value["nodeName"] : "Click to update";
-
-                // TODO: Move anchor tag creation to one.lib.form.
-                var aTag;
-                aTag = document.createElement("a");
-                aTag.privilege = data.privilege;
-                aTag.addEventListener("click", one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
-                aTag.addEventListener("mouseover", function(evt) {
-                    evt.target.style.cursor = "pointer";
-                }, false);
-                aTag.setAttribute("id", encodeURIComponent(value["nodeId"]));
-                aTag.switchDetails = value;
-                aTag.innerHTML = nodeNameEntry;
-                entry.push(aTag);
-                entry.push(value["nodeId"]);
-                entry.push(value["ports"]);
-                tr.entry = entry;
-                result.push(tr);
-            });
-            return result;
+        {
+          property: 'tierName',
+        label: 'Tier Name',
+        sortable: true
         },
-        popout : function(data) {
-            var result = [];
-            $.each(data.nodeData, function(key, value) {
-                var tr = {};
-                // fill up all the td's
-                var entry = [];
-                var nodenameentry = value["nodeName"] ? value["nodeName"] : "No name provided";
-                entry.push(nodenameentry);
-                entry.push(value["nodeId"]);
-                entry.push(value["tierName"]);
-                entry.push(value["mac"]);
-                entry.push(value["ports"]);
-                tr.entry = entry;
-                result.push(tr);
-            });
-            return result;
+        {
+          property: 'mac',
+        label: 'MAC Address',
+        sortable: true
+        },
+        {
+          property: 'ports',
+          label: 'Ports',
+          sortable: true
         }
+        ],
+          data: data.nodeData,
+          formatter: function(items) {
+            $.each(items, function (index, item) {
+              var ports = item.ports;
+              var portsMatch = ports.match(/<\/span>/g);
+              var portsLength = 0;
+              if (portsMatch != null) {
+                portsLength = portsMatch.length;
+              }
+              item.ports = '<span class="nodePorts" style="cursor: pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId="' + item.nodeId
+              + '" nodeName="' + item.nodeName
+              + '">' + portsLength +'</span>';
+            }); 
+          },
+          delay: 0
+        });
+        return source;
+      },
+      displayPorts: function(content){
+        var data=[];
+        var start=0;;
+        var finish=content.indexOf("<br>",start);
+        while(finish != -1){
+          data.push({"ports":content.substring(start,finish)});
+          start=finish+4
+            finish=content.indexOf("<br>",start);
+        }
+        var source = new StaticDataSource({
+          columns: [
+        {
+          property: 'ports',
+            label: 'Ports',
+            sortable: true
+        }
+        ],
+            data:data,
+            delay: 0
+        });
+
+        return source;
+      }
+    },
+    abridged : function(data) {
+      var result = [];
+      $.each(data.nodeData, function(key, value) {
+        var tr = {};
+        var entry = [];
+        var nodeNameEntry = value["nodeName"] ? value["nodeName"] : "Click to update";
+
+        // TODO: Move anchor tag creation to one.lib.form.
+        var aTag;
+        aTag = document.createElement("a");
+        aTag.privilege = data.privilege;
+        aTag.addEventListener("click", one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
+        aTag.addEventListener("mouseover", function(evt) {
+          evt.target.style.cursor = "pointer";
+        }, false);
+        aTag.setAttribute("id", encodeURIComponent(value["nodeId"]));
+        aTag.switchDetails = value;
+        aTag.innerHTML = nodeNameEntry;
+        entry.push(aTag);
+        entry.push(value["nodeId"]);
+        entry.push(value["ports"]);
+        tr.entry = entry;
+        result.push(tr);
+      });
+      return result;
+    },
+    popout : function(data) {
+      var result = [];
+      $.each(data.nodeData, function(key, value) {
+        var tr = {};
+        // fill up all the td's
+        var entry = [];
+        var nodenameentry = value["nodeName"] ? value["nodeName"] : "No name provided";
+        entry.push(nodenameentry);
+        entry.push(value["nodeId"]);
+        entry.push(value["tierName"]);
+        entry.push(value["mac"]);
+        entry.push(value["ports"]);
+        tr.entry = entry;
+        result.push(tr);
+      });
+      return result;
     }
+  }
 };
 
 one.f.switchmanager.subnetGatewayConfig = {
-    id: {
-        dashlet: {
-            addIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addIP",
-            addPorts: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addPorts",
-            removeIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_removeIP",
-            datagrid: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_datagrid",
-            selectAll: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_selectAll"
-        }, 
-        modal: {
-            modal: "one_f_switchmanager_subnetGatewayConfig_id_modal_modal",
-            ports : "one_f_switchmanager_subnetGatewayConfig_id_modal_ports",
-            save: "one_f_switchmanager_subnetGatewayConfig_id_modal_save",
-            remove: "one_f_switchmanager_subnetGatewayConfig_id_modal_remove",
-            cancel: "one_f_switchmanager_subnetGatewayConfig_id_modal_cancel",
-            form: {
-                name : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayname",
-                gatewayIPAddress : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayipaddress",
-                nodeId: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_nodeid",
-                ports: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_ports"
-            }
+  id: {
+    dashlet: {
+      addIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addIP",
+      addPorts: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addPorts",
+      removeIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_removeIP",
+      datagrid: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_datagrid",
+      selectAll: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_selectAll"
+    }, 
+    modal: {
+      modal: "one_f_switchmanager_subnetGatewayConfig_id_modal_modal",
+      ports : "one_f_switchmanager_subnetGatewayConfig_id_modal_ports",
+      save: "one_f_switchmanager_subnetGatewayConfig_id_modal_save",
+      remove: "one_f_switchmanager_subnetGatewayConfig_id_modal_remove",
+      cancel: "one_f_switchmanager_subnetGatewayConfig_id_modal_cancel",
+      form: {
+        name : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayname",
+        gatewayIPAddress : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayipaddress",
+        nodeId: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_nodeid",
+        ports: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_ports"
+      }
+    }
+  },
+  // device ajax calls
+  dashlet: function($dashlet) {
+    one.lib.dashlet.empty($dashlet);
+    $dashlet.append(one.lib.dashlet.header(one.f.dashlet.subnetGatewayConfig.name));
+    // Add gateway IP Address button
+    var url = one.f.switchmanager.rootUrl + "/subnets";
+    one.f.switchmanager.subnetGatewayConfig.ajax.main(url, {}, function(content) {
+
+      if (content.privilege === 'WRITE') {
+        var button = one.lib.dashlet.button.single("Add Gateway IP Address",
+          one.f.switchmanager.subnetGatewayConfig.id.dashlet.addIPAddress, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          var $modal = one.f.switchmanager.subnetGatewayConfig.modal.initialize.gateway();
+          $modal.modal();
+        });
+        $dashlet.append($button);
+
+        // Delete gateway ip address button
+        var button = one.lib.dashlet.button.single("Remove Gateway IP Address",
+          one.f.switchmanager.subnetGatewayConfig.id.dashlet.removeIPAddress, "btn-danger", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          var gatewaysToDelete = [];
+          var checkedCheckBoxes = $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).find("tbody input:checked")
+          checkedCheckBoxes.each(function(index, value) {
+            gatewaysToDelete.push(checkedCheckBoxes[index].id);
+          });
+        if (checkedCheckBoxes.size() === 0) {
+          return false;
         }
-    },
-    // device ajax calls
-    dashlet: function($dashlet) {
-        one.lib.dashlet.empty($dashlet);
-        $dashlet.append(one.lib.dashlet.header(one.f.dashlet.subnetGatewayConfig.name));
-        // Add gateway IP Address button
-        var url = one.f.switchmanager.rootUrl + "/subnets";
-        one.f.switchmanager.subnetGatewayConfig.ajax.main(url, {}, function(content) {
-
-            if (content.privilege === 'WRITE') {
-                var button = one.lib.dashlet.button.single("Add Gateway IP Address",
-                    one.f.switchmanager.subnetGatewayConfig.id.dashlet.addIPAddress, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    var $modal = one.f.switchmanager.subnetGatewayConfig.modal.initialize.gateway();
-                    $modal.modal();
-                });
-                $dashlet.append($button);
-
-                // Delete gateway ip address button
-                var button = one.lib.dashlet.button.single("Remove Gateway IP Address",
-                one.f.switchmanager.subnetGatewayConfig.id.dashlet.removeIPAddress, "btn-danger", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    var gatewaysToDelete = [];
-                    var checkedCheckBoxes = $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).find("tbody input:checked")
-                    checkedCheckBoxes.each(function(index, value) {
-                        gatewaysToDelete.push(checkedCheckBoxes[index].id);
-                    });
-                    if (checkedCheckBoxes.size() === 0) {
-                        return false;
-                    }
-                    one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.dialog(gatewaysToDelete)
-                });
-                $dashlet.append($button);
-
-                // Add Ports button
-                var button = one.lib.dashlet.button.single("Add Ports",
-                    one.f.switchmanager.subnetGatewayConfig.id.dashlet.addPorts, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    if (one.f.switchmanager.subnetGatewayConfig.registry.gateways.length === 0) {
-                      alert('No Gateways Exist');
-                      return false;
-                    }
-                    var $modal = one.f.switchmanager.subnetGatewayConfig.modal.initialize.ports();
-                    $modal.modal();
-                });
-                $dashlet.append($button);
+        one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.dialog(gatewaysToDelete)
+        });
+        $dashlet.append($button);
+
+        // Add Ports button
+        var button = one.lib.dashlet.button.single("Add Ports",
+            one.f.switchmanager.subnetGatewayConfig.id.dashlet.addPorts, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          if (one.f.switchmanager.subnetGatewayConfig.registry.gateways.length === 0) {
+            alert('No Gateways Exist');
+            return false;
+          }
+          var $modal = one.f.switchmanager.subnetGatewayConfig.modal.initialize.ports();
+          $modal.modal();
+        });
+        $dashlet.append($button);
+      }
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.switchmanager.subnetGatewayConfig.data.devicesgrid(content);
+      $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
+        .on("loaded", function() {
+          $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll).click(function() {
+            $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
+              $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll).is(':checked'));
+          });
+          $(".subnetGatewayConfig").click(function(e){
+            if (!$('.subnetGatewayConfig[type=checkbox]:not(:checked)').length) {
+              $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll)
+            .prop("checked",
+              true);
+            } else {
+              $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll)
+            .prop("checked",
+              false);
             }
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.switchmanager.subnetGatewayConfig.data.devicesgrid(content);
-            $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
-            .on("loaded", function() {
-                $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll).click(function() {
-                    $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
-                        $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll).is(':checked'));
-                });
-                $(".subnetGatewayConfig").click(function(e){
-                    if (!$('.subnetGatewayConfig[type=checkbox]:not(:checked)').length) {
-                        $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                          true);
-                    } else {
-                        $("#"+one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                         false);
-                    }
-                    e.stopPropagation();
-                });
-             });
+            e.stopPropagation();
+          });
         });
+    });
+  },
+  ajax : {
+    main : function(url, requestData, callback) {
+      $.getJSON(url, requestData, function(data) {
+        callback(data);
+      });
+    }
+  },
+  registry: {},
+  modal : {
+    initialize: {
+      gateway: function() {
+        var h3 = "Add Gateway IP Address";
+        var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
+        // bind save button
+        $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
+          one.f.switchmanager.subnetGatewayConfig.modal.save.gateway($modal);
+        });
+        var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.gateway();
+        one.lib.modal.inject.body($modal, $body);
+        return $modal;
+      },
+      ports: function() {
+        var h3 = "Add Ports";
+        var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.ports, h3, "", footer);
+        // bind save button
+        $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
+          one.f.switchmanager.subnetGatewayConfig.modal.save.ports($modal);
+        });
+
+        // TODO: Change to subnetGateway instead.
+        one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
+          var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.ports(nodes, nodeports);
+          one.lib.modal.inject.body($modal, $body);
+        });
+        return $modal;
+      }
     },
-    ajax : {
-        main : function(url, requestData, callback) {
-            $.getJSON(url, requestData, function(data) {
-                callback(data);
+    save: {
+      gateway: function($modal) {
+        var result = {};
+        result['gatewayName'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
+        if(!one.f.switchmanager.validateName(result['gatewayName'])) {
+          alert("Gateway name can contain upto 255 characters");
+          return;
+        }
+        result['gatewayIPAddress'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress, $modal).val();
+        one.f.switchmanager.subnetGatewayConfig.modal.ajax.gateway(result, 
+            function(response) {
+              if(response.status == true) {
+                $modal.modal('hide');
+                one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+              } else {
+                alert(response.message);
+              }
             });
+      },
+      ports: function($modal) {
+        var result = {};
+        var gatewayRegistryIndex = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
+        result['portsName'] = one.f.switchmanager.subnetGatewayConfig.registry.gateways[gatewayRegistryIndex];
+        result['nodeId'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId, $modal).val();
+        result['ports'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports, $modal).val();
+        if(!result['portsName'] || result['portsName'] == "") {
+          alert("No gateway chosen. Cannot add port");
+          return;
         }
-    },
-    registry: {},
-    modal : {
-        initialize: {
-            gateway: function() {
-                var h3 = "Add Gateway IP Address";
-                var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
-                // bind save button
-                $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
-                    one.f.switchmanager.subnetGatewayConfig.modal.save.gateway($modal);
-                });
-                var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.gateway();
-                one.lib.modal.inject.body($modal, $body);
-                return $modal;
-            },
-            ports: function() {
-                var h3 = "Add Ports";
-                var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.ports, h3, "", footer);
-                // bind save button
-                $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
-                    one.f.switchmanager.subnetGatewayConfig.modal.save.ports($modal);
-                });
-                
-                // TODO: Change to subnetGateway instead.
-                one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
-                    var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.ports(nodes, nodeports);
-                    one.lib.modal.inject.body($modal, $body);
-                });
-                return $modal;
-            }
-        },
-        save: {
-            gateway: function($modal) {
-                var result = {};
-                result['gatewayName'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
-                if(!one.f.switchmanager.validateName(result['gatewayName'])) {
-                    alert("Gateway name can contain upto 255 characters");
-                    return;
-                }
-                result['gatewayIPAddress'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress, $modal).val();
-                one.f.switchmanager.subnetGatewayConfig.modal.ajax.gateway(result, 
-                    function(response) {
-                        if(response.status == true) {
-                            $modal.modal('hide');
-                            one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
-                        } else {
-                            alert(response.message);
-                        }
-                    });
-            },
-            ports: function($modal) {
-                var result = {};
-                var gatewayRegistryIndex = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
-                result['portsName'] = one.f.switchmanager.subnetGatewayConfig.registry.gateways[gatewayRegistryIndex];
-                result['nodeId'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId, $modal).val();
-                result['ports'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports, $modal).val();
-                if(!result['portsName'] || result['portsName'] == "") {
-                    alert("No gateway chosen. Cannot add port");
-                    return;
-                }
-                if(!result['nodeId'] || result['nodeId'] == "") {
-                    alert("Please select a node.");
-                    return;
-                }
-                if(!result['ports'] || result['ports'] == "") {
-                    alert("Please choose a port.");
-                    return;
-                }
-                one.f.switchmanager.subnetGatewayConfig.modal.ajax.ports(result, 
-                    function(response) {
-                        if(response.status == true) {
-                            $modal.modal('hide');
-                            one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
-                        } else {
-                            alert(response.message);
-                        }
-                    });
-            }
-        },
-        body: {
-            gateway: function() {
-                var $form = $(document.createElement('form'));
-                var $fieldset = $(document.createElement('fieldset'));
-                // gateway name
-                var $label = one.lib.form.label("Name");
-                var $input = one.lib.form.input("Name");
-                $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
-                $fieldset.append($label).append($input);
-                // gateway IP Mask 
-                var $label = one.lib.form.label("Gateway IP Address/Mask");
-                var $input = one.lib.form.input("Gateway IP Address/Mask");
-                var $help = one.lib.form.help('Example: 192.168.10.254/16');
-                $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress);
-                $fieldset.append($label).append($input).append($help);
-                
-                $form.append($fieldset);
-                return $form;
-            },
-            ports: function(nodes, nodeports) {
-                var $form = $(document.createElement('form'));
-                var $fieldset = $(document.createElement('fieldset'));
-                // gateways drop down
-                var $label = one.lib.form.label("Gateway Name");
-                var $select = one.lib.form.select.create(one.f.switchmanager.subnetGatewayConfig.registry.gateways);
-                $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
-                one.lib.form.select.prepend($select, { '' : 'Please Select a Gateway' });
-                $select.val($select.find("option:first").val());
-                $fieldset.append($label).append($select);
-
-                // node ID
-                var $label = one.lib.form.label("Node ID");
-                var $select = one.lib.form.select.create(nodes);
-                $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId);
-                one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
-                $select.val($select.find("option:first").val());
-                $fieldset.append($label).append($select);
-
-                // bind onchange
-                $select.change(function() {
-                    // retrieve port value
-                    var node = $(this).find('option:selected').attr('value');
-                    one.f.switchmanager.subnetGatewayConfig.registry['currentNode'] = node;
-                    var $ports = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
-                    var ports = nodeports[node];
-                    var options = {};
-                    $(ports).each(function(idx, val) {
-                        options[val.internalPortName] = val.portName+' ('+val.portId+')'; 
-                    });
-                    one.lib.form.select.inject($ports, options);
-                    one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
-                    $ports.val($ports.find("option:first").val());
-                });
-
-                // ports
-                var $label = one.lib.form.label("Select Port");
-                var $select = one.lib.form.select.create();
-                one.lib.form.select.prepend($select, { '' : 'Please Select a Port' });
-                $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
-                $fieldset.append($label).append($select);
-                
-                $form.append($fieldset);
-                return $form;
-            }
-        },
-        ajax: {
-            gateway: function(requestData, callback) {
-                $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/add", requestData, function(data) {
-                    callback(data);
+        if(!result['nodeId'] || result['nodeId'] == "") {
+          alert("Please select a node.");
+          return;
+        }
+        if(!result['ports'] || result['ports'] == "") {
+          alert("Please choose a port.");
+          return;
+        }
+        one.f.switchmanager.subnetGatewayConfig.modal.ajax.ports(result, 
+            function(response) {
+              if(response.status == true) {
+                $modal.modal('hide');
+                one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+              } else {
+                alert(response.message);
+              }
             });
-            },
-            ports: function(requestData, callback) {
-                $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/ports/add", requestData, function(data) {
-                    callback(data);
+      }
+    },
+    body: {
+      gateway: function() {
+        var $form = $(document.createElement('form'));
+        var $fieldset = $(document.createElement('fieldset'));
+        // gateway name
+        var $label = one.lib.form.label("Name");
+        var $input = one.lib.form.input("Name");
+        $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
+        $fieldset.append($label).append($input);
+        // gateway IP Mask 
+        var $label = one.lib.form.label("Gateway IP Address/Mask");
+        var $input = one.lib.form.input("Gateway IP Address/Mask");
+        var $help = one.lib.form.help('Example: 192.168.10.254/16');
+        $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress);
+        $fieldset.append($label).append($input).append($help);
+
+        $form.append($fieldset);
+        return $form;
+      },
+      ports: function(nodes, nodeports) {
+        var $form = $(document.createElement('form'));
+        var $fieldset = $(document.createElement('fieldset'));
+        // gateways drop down
+        var $label = one.lib.form.label("Gateway Name");
+        var $select = one.lib.form.select.create(one.f.switchmanager.subnetGatewayConfig.registry.gateways);
+        $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
+        one.lib.form.select.prepend($select, { '' : 'Please Select a Gateway' });
+        $select.val($select.find("option:first").val());
+        $fieldset.append($label).append($select);
+
+        // node ID
+        var $label = one.lib.form.label("Node ID");
+        var $select = one.lib.form.select.create(nodes);
+        $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId);
+        one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+        $select.val($select.find("option:first").val());
+        $fieldset.append($label).append($select);
+
+        // bind onchange
+        $select.change(function() {
+          // retrieve port value
+          var node = $(this).find('option:selected').attr('value');
+          one.f.switchmanager.subnetGatewayConfig.registry['currentNode'] = node;
+          var $ports = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
+          var ports = nodeports[node];
+          var options = {};
+          $(ports).each(function(idx, val) {
+            options[val.internalPortName] = val.portName+' ('+val.portId+')'; 
+              });
+            one.lib.form.select.inject($ports, options);
+            one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+            $ports.val($ports.find("option:first").val());
             });
-            }
-        },
-        footer : function() {
-            var footer = [];
-            var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.subnetGatewayConfig.id.modal.save, "btn-primary", "");
-            var $saveButton = one.lib.dashlet.button.button(saveButton);
-            footer.push($saveButton);
-            return footer;
-        },
-        removeMultiple: {
-            dialog: function(gatewaysToDelete) {
-                var h3 = 'Remove Gateway IP Address';
-                
-                var footer = one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.footer();
-                var $body = one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.body(gatewaysToDelete);
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, $body, footer);
-
-                // bind close button
-                $('#'+one.f.switchmanager.subnetGatewayConfig.id.modal.cancel, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-
-                // bind remove rule button
-                $('#'+one.f.switchmanager.subnetGatewayConfig.id.modal.remove, $modal).click(this, function(e) {
-                    var requestData = {};
-                    if (gatewaysToDelete.length > 0) {
-                        requestData["gatewaysToDelete"] = gatewaysToDelete.toString();
-                        var url = one.f.switchmanager.rootUrl + "/subnetGateway/delete";
-                        one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
-                            $modal.modal('hide');
-                            if (response.status == true) {
-                                // refresh dashlet by passing dashlet div as param 
-                                one.lib.alert("Gateway IP Address(es) successfully removed");
-                            } else {
-                                alert(response.message);
-                            }
-                            one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
-                        });
-                    }
-                });
-                $modal.modal();
-            },
-            footer : function() {
-                var footer = [];
-                var remove = one.lib.dashlet.button.single('Remove Gateway IP Address',one.f.switchmanager.subnetGatewayConfig.id.modal.remove, 'btn-danger', '');
-                var $remove = one.lib.dashlet.button.button(remove);
-                footer.push($remove);
-
-                var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.subnetGatewayConfig.id.modal.cancel, '', '');
-                var $cancel = one.lib.dashlet.button.button(cancel);
-                footer.push($cancel);
-
-                return footer;
-            },
-            body : function (gatewayList) {
-                var $p = $(document.createElement('p'));
-                var p = 'Remove the following Gateway IP Address(es)?';
-                //creata a BS label for each rule and append to list
-                $(gatewayList).each(function(){
-                    var $span = $(document.createElement('span'));
-                    $span.append(this);
-                    p += '<br/>' + $span[0].outerHTML;
-                });
-                $p.append(p);
-                return $p;
-            }
-        }
+
+          // ports
+          var $label = one.lib.form.label("Select Port");
+          var $select = one.lib.form.select.create();
+          one.lib.form.select.prepend($select, { '' : 'Please Select a Port' });
+          $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
+          $fieldset.append($label).append($select);
+
+          $form.append($fieldset);
+          return $form;
+      }
     },
-    // data functions
-    data : {
-        devicesgrid: function(data) {
-            one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'selector',
-                            label: "<input type='checkbox'  id='"
-                                +one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll+"'/>",
-                            sortable: false
-                        },
-                        {
-                            property: 'name',
-                            label: 'Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'subnet',
-                            label: 'Gateway IP Address/Mask',
-                            sortable: true
-                        },
-                        {
-                            property: 'nodePorts',
-                            label: 'Ports',
-                            sortable: false
-                        }
-                    ],
-                    data: data.nodeData,
-                    formatter: function(items) {
-                        $.each(items, function(index, tableRow) {
-                            tableRow["selector"] = '<input type="checkbox" class="subnetGatewayConfig" id="'
-                                + tableRow["name"] + '"></input>';
-                            var json = tableRow["nodePorts"];
-                            var nodePorts = JSON.parse(json);
-                            var nodePortHtml = "<div>";
-                            $.each(nodePorts, function(index, nodePort) {
-                                nodePortHtml += nodePort["nodePortName"] + " @ " + nodePort["nodeName"];
-                                nodePortHtml += "&nbsp;";
-                                nodePortHtml += '<a href="#" id="' + encodeURIComponent(nodePort["nodePortId"]) +
-                                    '" gatewayName="' + tableRow["name"] +
-                                    '" onclick="javascript:one.f.switchmanager.subnetGatewayConfig.actions.deleteNodePort(this);">Remove</a>';
-                                nodePortHtml += "<br/>";
-                            });
-                            nodePortHtml += "</div>";
-                            tableRow["nodePorts"] = nodePortHtml;
-                        });
-
-                    },
-                    delay: 0
-                });
-                // populate the registry with subnet names
-                one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
-                $.each(data.nodeData, function(key, value) {
-                    one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
-                });
-                return source;          
-        },
-        devices : function(data) {
-            var result = [];
-            one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
-            $.each(data.nodeData, function(key, value) {
-                var tr = {};
-                // fill up all the td's
-                var subnetConfigObject = $.parseJSON(value["json"]);
-                var nodePorts = subnetConfigObject.nodePorts;
-                var $nodePortsContainer = $(document.createElement("div"));
-                
-                for(var i = 0; i < nodePorts.length; i++) {
-                    var nodePort = nodePorts[i];
-                    $nodePortsContainer.append(nodePort + " ");
-                    // add delete anchor tag to delete ports
-                    var aTag = document.createElement("a");
-                    aTag.setAttribute("id", encodeURIComponent(nodePort));
-                    aTag.gatewayName = value["name"];
-                    aTag.addEventListener("click", function(evt) {
-                        var htmlPortAnchor = evt.target;
-                        var requestData = {};
-                        requestData["gatewayName"] = evt.target.gatewayName;
-                        requestData["nodePort"] = decodeURIComponent(evt.target.id);
-                        // make ajax call to delete port
-                        var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
-                        one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
-                            if(response.status == true) {
-                                // refresh dashlet by passing dashlet div as param
-                                one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
-                            } else {
-                                alert(response.message);
-                            }
-                        });
-                        
-                    });
-                    aTag.addEventListener("mouseover", function(evt) {
-                        evt.target.style.cursor = "pointer";
-                    }, false);
-                    aTag.innerHTML = "Remove";
-                    $nodePortsContainer.append(aTag);
-                    $nodePortsContainer.append("<br/>");
-                }
-
-                // store gateways in the registry so that they can be used in the add ports popup
-                one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
-                var entry = [];
-                var checkbox = document.createElement("input");
-                checkbox.setAttribute("type", "checkbox");
-                checkbox.setAttribute("id", value["name"]);
-                entry.push(checkbox);
-                entry.push(value["name"]);
-                entry.push(value["subnet"]);
-                entry.push($nodePortsContainer);
-                tr.entry = entry;
-                result.push(tr);
+    ajax: {
+      gateway: function(requestData, callback) {
+        $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/add", requestData, function(data) {
+          callback(data);
+        });
+      },
+      ports: function(requestData, callback) {
+        $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/ports/add", requestData, function(data) {
+          callback(data);
+        });
+      }
+    },
+    footer : function() {
+      var footer = [];
+      var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.subnetGatewayConfig.id.modal.save, "btn-primary", "");
+      var $saveButton = one.lib.dashlet.button.button(saveButton);
+      footer.push($saveButton);
+      return footer;
+    },
+    removeMultiple: {
+      dialog: function(gatewaysToDelete) {
+        var h3 = 'Remove Gateway IP Address';
+
+        var footer = one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.footer();
+        var $body = one.f.switchmanager.subnetGatewayConfig.modal.removeMultiple.body(gatewaysToDelete);
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, $body, footer);
+
+        // bind close button
+        $('#'+one.f.switchmanager.subnetGatewayConfig.id.modal.cancel, $modal).click(function() {
+          $modal.modal('hide');
+        });
+
+        // bind remove rule button
+        $('#'+one.f.switchmanager.subnetGatewayConfig.id.modal.remove, $modal).click(this, function(e) {
+          var requestData = {};
+          if (gatewaysToDelete.length > 0) {
+            requestData["gatewaysToDelete"] = gatewaysToDelete.toString();
+            var url = one.f.switchmanager.rootUrl + "/subnetGateway/delete";
+            one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
+              $modal.modal('hide');
+              if (response.status == true) {
+                // refresh dashlet by passing dashlet div as param 
+                one.lib.alert("Gateway IP Address(es) successfully removed");
+              } else {
+                alert(response.message);
+              }
+              one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
             });
-            return result;
-        }
+          }
+        });
+        $modal.modal();
+      },
+      footer : function() {
+        var footer = [];
+        var remove = one.lib.dashlet.button.single('Remove Gateway IP Address',one.f.switchmanager.subnetGatewayConfig.id.modal.remove, 'btn-danger', '');
+        var $remove = one.lib.dashlet.button.button(remove);
+        footer.push($remove);
+
+        var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.subnetGatewayConfig.id.modal.cancel, '', '');
+        var $cancel = one.lib.dashlet.button.button(cancel);
+        footer.push($cancel);
+
+        return footer;
+      },
+      body : function (gatewayList) {
+        var $p = $(document.createElement('p'));
+        var p = 'Remove the following Gateway IP Address(es)?';
+        //creata a BS label for each rule and append to list
+        $(gatewayList).each(function(){
+          var $span = $(document.createElement('span'));
+          $span.append(this);
+          p += '<br/>' + $span[0].outerHTML;
+        });
+        $p.append(p);
+        return $p;
+      }
+    }
+  },
+  // data functions
+  data : {
+    devicesgrid: function(data) {
+      one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'selector',
+          label: "<input type='checkbox'  id='"
+        +one.f.switchmanager.subnetGatewayConfig.id.dashlet.selectAll+"'/>",
+          sortable: false
+      },
+          {
+            property: 'name',
+          label: 'Name',
+          sortable: true
+          },
+          {
+            property: 'subnet',
+          label: 'Gateway IP Address/Mask',
+          sortable: true
+          },
+          {
+            property: 'nodePorts',
+          label: 'Ports',
+          sortable: false
+          }
+      ],
+        data: data.nodeData,
+        formatter: function(items) {
+          $.each(items, function(index, tableRow) {
+            tableRow["selector"] = '<input type="checkbox" class="subnetGatewayConfig" id="'
+            + tableRow["name"] + '"></input>';
+          var json = tableRow["nodePorts"];
+          var nodePorts = JSON.parse(json);
+          var nodePortHtml = "<div>";
+          $.each(nodePorts, function(index, nodePort) {
+            nodePortHtml += nodePort["nodePortName"] + " @ " + nodePort["nodeName"];
+            nodePortHtml += "&nbsp;";
+            nodePortHtml += '<a href="#" id="' + encodeURIComponent(nodePort["nodePortId"]) +
+            '" gatewayName="' + tableRow["name"] +
+            '" onclick="javascript:one.f.switchmanager.subnetGatewayConfig.actions.deleteNodePort(this);">Remove</a>';
+          nodePortHtml += "<br/>";
+          });
+          nodePortHtml += "</div>";
+          tableRow["nodePorts"] = nodePortHtml;
+          });
+
+        },
+        delay: 0
+      });
+      // populate the registry with subnet names
+      one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+      $.each(data.nodeData, function(key, value) {
+        one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
+      });
+      return source;          
     },
-    actions: {
-        deleteNodePort: function(htmlPortAnchor) {
+    devices : function(data) {
+      var result = [];
+      one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+      $.each(data.nodeData, function(key, value) {
+        var tr = {};
+        // fill up all the td's
+        var subnetConfigObject = $.parseJSON(value["json"]);
+        var nodePorts = subnetConfigObject.nodePorts;
+        var $nodePortsContainer = $(document.createElement("div"));
+
+        for(var i = 0; i < nodePorts.length; i++) {
+          var nodePort = nodePorts[i];
+          $nodePortsContainer.append(nodePort + " ");
+          // add delete anchor tag to delete ports
+          var aTag = document.createElement("a");
+          aTag.setAttribute("id", encodeURIComponent(nodePort));
+          aTag.gatewayName = value["name"];
+          aTag.addEventListener("click", function(evt) {
+            var htmlPortAnchor = evt.target;
             var requestData = {};
-            requestData["gatewayName"] = htmlPortAnchor.getAttribute("gatewayName");
-            requestData["nodePort"] = decodeURIComponent(htmlPortAnchor.id);
+            requestData["gatewayName"] = evt.target.gatewayName;
+            requestData["nodePort"] = decodeURIComponent(evt.target.id);
             // make ajax call to delete port
             var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
             one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
-                if(response.status == true) {
-                    // refresh dashlet by passing dashlet div as param
-                    one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
-                } else {
-                    alert(response.message);
-                }
+              if(response.status == true) {
+                // refresh dashlet by passing dashlet div as param
+                one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+              } else {
+                alert(response.message);
+              }
             });
+
+          });
+          aTag.addEventListener("mouseover", function(evt) {
+            evt.target.style.cursor = "pointer";
+          }, false);
+          aTag.innerHTML = "Remove";
+          $nodePortsContainer.append(aTag);
+          $nodePortsContainer.append("<br/>");
+        }
+
+        // store gateways in the registry so that they can be used in the add ports popup
+        one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
+        var entry = [];
+        var checkbox = document.createElement("input");
+        checkbox.setAttribute("type", "checkbox");
+        checkbox.setAttribute("id", value["name"]);
+        entry.push(checkbox);
+        entry.push(value["name"]);
+        entry.push(value["subnet"]);
+        entry.push($nodePortsContainer);
+        tr.entry = entry;
+        result.push(tr);
+      });
+      return result;
+    }
+  },
+  actions: {
+    deleteNodePort: function(htmlPortAnchor) {
+      var requestData = {};
+      requestData["gatewayName"] = htmlPortAnchor.getAttribute("gatewayName");
+      requestData["nodePort"] = decodeURIComponent(htmlPortAnchor.id);
+      // make ajax call to delete port
+      var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
+      one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
+        if(response.status == true) {
+          // refresh dashlet by passing dashlet div as param
+          one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+        } else {
+          alert(response.message);
         }
+      });
     }
+  }
 }
 
 one.f.switchmanager.staticRouteConfig = {
-    id: {
-        dashlet: {
-            add: "one_f_switchmanager_staticRouteConfig_id_dashlet_add",
-            remove: "one_f_switchmanager_staticRouteConfig_id_dashlet_remove",
-            datagrid: "one_f_switchmanager_staticRouteConfig_id_dashlet_datagrid",
-            selectAll: "one_f_switchmanager_staticRouteConfig_id_dashlet_selectAll"
-        }, 
-        modal: {
-            modal: "one_f_switchmanager_staticRouteConfig_id_modal_modal",
-            save: "one_f_switchmanager_staticRouteConfig_id_modal_save",
-            cancel: "one_f_switchmanager_staticRouteConfig_id_modal_cancel",
-            remove: "one_f_switchmanager_staticRouteConfig_id_modal_remove",
-            form: {
-                routeName : "one_f_switchmanager_staticRouteConfig_id_modal_form_routename",
-                staticRoute : "one_f_switchmanager_staticRouteConfig_id_modal_form_staticroute",
-                nextHop : "one_f_switchmanager_staticRouteConfig_id_modal_form_nexthop",
+  id: {
+    dashlet: {
+      add: "one_f_switchmanager_staticRouteConfig_id_dashlet_add",
+      remove: "one_f_switchmanager_staticRouteConfig_id_dashlet_remove",
+      datagrid: "one_f_switchmanager_staticRouteConfig_id_dashlet_datagrid",
+      selectAll: "one_f_switchmanager_staticRouteConfig_id_dashlet_selectAll"
+    }, 
+    modal: {
+      modal: "one_f_switchmanager_staticRouteConfig_id_modal_modal",
+      save: "one_f_switchmanager_staticRouteConfig_id_modal_save",
+      cancel: "one_f_switchmanager_staticRouteConfig_id_modal_cancel",
+      remove: "one_f_switchmanager_staticRouteConfig_id_modal_remove",
+      form: {
+        routeName : "one_f_switchmanager_staticRouteConfig_id_modal_form_routename",
+        staticRoute : "one_f_switchmanager_staticRouteConfig_id_modal_form_staticroute",
+        nextHop : "one_f_switchmanager_staticRouteConfig_id_modal_form_nexthop",
+      }
+    }
+  },
+  dashlet: function($dashlet) {
+    one.lib.dashlet.empty($dashlet);
+    var url = one.f.switchmanager.rootUrl + "/staticRoutes";
+    one.f.switchmanager.staticRouteConfig.ajax.main(url, {}, function(content) {
+
+      if (content.privilege === 'WRITE') {
+        // Add static route button
+        var button = one.lib.dashlet.button.single("Add Static Route", one.f.switchmanager.staticRouteConfig.id.dashlet.add, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          var $modal = one.f.switchmanager.staticRouteConfig.modal.initialize();
+          $modal.modal();
+        });
+        $dashlet.append(one.lib.dashlet.header(one.f.dashlet.staticRouteConfig.name));
+        $dashlet.append($button);
+
+        // Delete static route button
+        var button = one.lib.dashlet.button.single("Remove Static Route", one.f.switchmanager.staticRouteConfig.id.dashlet.remove, "btn-danger", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          var routesToDelete = [];
+          var checkedCheckBoxes = $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).find("tbody input:checked");
+          checkedCheckBoxes.each(function(index, value) {
+            routesToDelete.push(checkedCheckBoxes[index].id);
+          });
+          if (checkedCheckBoxes.size() === 0) {
+            return false;
+          }
+          one.f.switchmanager.staticRouteConfig.modal.removeMultiple.dialog(routesToDelete);
+        });
+        $dashlet.append($button);
+      }
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.switchmanager.staticRouteConfig.data.staticRouteConfigGrid(content);
+      $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
+        .on("loaded", function() {
+          $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll).click(function() {
+            $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
+              $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll).is(':checked'));
+          });
+          $(".staticRoute").click(function(e){
+            if (!$('.staticRoute[type=checkbox]:not(:checked)').length) {
+              $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll)
+            .prop("checked",
+              true);
+            } else {
+              $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll)
+            .prop("checked",
+              false);
             }
+            e.stopPropagation();
+          });
+        });
+    });
+  },
+  // device ajax calls
+  ajax : {
+    main : function(url, requestData, callback) {
+      $.getJSON(url, requestData, function(data) {
+        callback(data);
+      });
+    }
+  },
+  registry: {},
+  modal : {
+    initialize: function() {
+      var h3 = "Add Static Route";
+      var footer = one.f.switchmanager.staticRouteConfig.modal.footer();
+      var $modal = one.lib.modal.spawn(one.f.switchmanager.staticRouteConfig.id.modal.modal, h3, "", footer);
+      // bind save button
+      $('#' + one.f.switchmanager.staticRouteConfig.id.modal.save, $modal).click(function() {
+        one.f.switchmanager.staticRouteConfig.modal.save($modal);
+      });
+      var $body = one.f.switchmanager.staticRouteConfig.modal.body();
+      one.lib.modal.inject.body($modal, $body);
+      return $modal;
+    },
+    save: function($modal) {
+      var result = {};
+      result['routeName'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.routeName, $modal).val();
+      result['staticRoute'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute, $modal).val();
+      result['nextHop'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop, $modal).val();
+      one.f.switchmanager.staticRouteConfig.modal.ajax.staticRouteConfig(result, function(response) {
+        if(response.status == true) {
+          $modal.modal('hide');
+          // refresh dashlet by passing dashlet div as param
+          one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
+        } else {
+          // TODO: Show error message in a error message label instead.
+          alert(response.message);
         }
+      });
     },
-    dashlet: function($dashlet) {
-        one.lib.dashlet.empty($dashlet);
-        var url = one.f.switchmanager.rootUrl + "/staticRoutes";
-        one.f.switchmanager.staticRouteConfig.ajax.main(url, {}, function(content) {
-
-            if (content.privilege === 'WRITE') {
-                // Add static route button
-                var button = one.lib.dashlet.button.single("Add Static Route", one.f.switchmanager.staticRouteConfig.id.dashlet.add, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    var $modal = one.f.switchmanager.staticRouteConfig.modal.initialize();
-                    $modal.modal();
-                });
-                $dashlet.append(one.lib.dashlet.header(one.f.dashlet.staticRouteConfig.name));
-                $dashlet.append($button);
-
-                // Delete static route button
-                var button = one.lib.dashlet.button.single("Remove Static Route", one.f.switchmanager.staticRouteConfig.id.dashlet.remove, "btn-danger", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    var routesToDelete = [];
-                    var checkedCheckBoxes = $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).find("tbody input:checked");
-                    checkedCheckBoxes.each(function(index, value) {
-                        routesToDelete.push(checkedCheckBoxes[index].id);
-                    });
-                    if (checkedCheckBoxes.size() === 0) {
-                        return false;
-                    }
-                    one.f.switchmanager.staticRouteConfig.modal.removeMultiple.dialog(routesToDelete);
-                });
-                $dashlet.append($button);
-            }
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.switchmanager.staticRouteConfig.data.staticRouteConfigGrid(content);
-            $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
-            .on("loaded", function() {
-                $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll).click(function() {
-                    $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
-                        $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll).is(':checked'));
-                });
-                $(".staticRoute").click(function(e){
-                    if (!$('.staticRoute[type=checkbox]:not(:checked)').length) {
-                        $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                          true);
-                    } else {
-                        $("#"+one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                         false);
-                    }
-                    e.stopPropagation();
-                });
-             });
+    body: function() {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      // static route name
+      var $label = one.lib.form.label("Name");
+      var $input = one.lib.form.input("Name");
+      $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.routeName);
+      $fieldset.append($label).append($input);
+      // static route IP Mask 
+      var $label = one.lib.form.label("Static Route");
+      var $input = one.lib.form.input("Static Route");
+      var $help = one.lib.form.help('Example: 53.55.0.0/16');
+      $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute);
+      $fieldset.append($label).append($input).append($help);
+      // static route IP Mask 
+      var $label = one.lib.form.label("Next Hop");
+      var $input = one.lib.form.input("Next Hop");
+      var $help = one.lib.form.help('Example: 192.168.10.254');
+      $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop);
+      $fieldset.append($label).append($input).append($help);
+      // return
+      $form.append($fieldset);
+      return $form;
+    },
+    ajax: {
+      staticRouteConfig: function(requestData, callback) {
+        $.getJSON(one.f.switchmanager.rootUrl + "/staticRoute/add", requestData, function(data) {
+          callback(data);
         });
+      }
     },
-    // device ajax calls
-    ajax : {
-        main : function(url, requestData, callback) {
-            $.getJSON(url, requestData, function(data) {
-                callback(data);
-            });
-        }
+    data : {
+
     },
-    registry: {},
-    modal : {
-        initialize: function() {
-            var h3 = "Add Static Route";
-            var footer = one.f.switchmanager.staticRouteConfig.modal.footer();
-            var $modal = one.lib.modal.spawn(one.f.switchmanager.staticRouteConfig.id.modal.modal, h3, "", footer);
-            // bind save button
-            $('#' + one.f.switchmanager.staticRouteConfig.id.modal.save, $modal).click(function() {
-                one.f.switchmanager.staticRouteConfig.modal.save($modal);
+    footer : function() {
+      var footer = [];
+      var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.staticRouteConfig.id.modal.save, "btn-primary", "");
+      var $saveButton = one.lib.dashlet.button.button(saveButton);
+      footer.push($saveButton);
+      return footer;
+    },
+    removeMultiple: {
+      dialog: function(routesToDelete) {
+        var h3 = 'Remove Static Route';
+
+        var footer = one.f.switchmanager.staticRouteConfig.modal.removeMultiple.footer();
+        var $body = one.f.switchmanager.staticRouteConfig.modal.removeMultiple.body(routesToDelete);
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.staticRouteConfig.id.modal.modal, h3, $body, footer);
+
+        // bind close button
+        $('#'+one.f.switchmanager.staticRouteConfig.id.modal.cancel, $modal).click(function() {
+          $modal.modal('hide');
+        });
+
+        // bind remove rule button
+        $('#'+one.f.switchmanager.staticRouteConfig.id.modal.remove, $modal).click(this, function(e) {
+          if (routesToDelete.length > 0) {
+            var requestData = {};
+            requestData["routesToDelete"] = routesToDelete.toString();
+            var url = one.f.switchmanager.rootUrl + "/staticRoute/delete";
+            one.f.switchmanager.staticRouteConfig.ajax.main(url, requestData, function(response) {
+              $modal.modal('hide');
+              if (response.status == true) {
+                // refresh dashlet by passing dashlet div as param 
+                one.lib.alert("Static Route(s) successfully removed");
+              } else {
+                alert(response.message);
+              }
+              one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
             });
-            var $body = one.f.switchmanager.staticRouteConfig.modal.body();
-            one.lib.modal.inject.body($modal, $body);
-            return $modal;
-        },
-        save: function($modal) {
-            var result = {};
-            result['routeName'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.routeName, $modal).val();
-            result['staticRoute'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute, $modal).val();
-            result['nextHop'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop, $modal).val();
-            one.f.switchmanager.staticRouteConfig.modal.ajax.staticRouteConfig(result, function(response) {
-                    if(response.status == true) {
-                        $modal.modal('hide');
-                        // refresh dashlet by passing dashlet div as param
-                        one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
-                    } else {
-                        // TODO: Show error message in a error message label instead.
-                        alert(response.message);
-                    }
-                });
-        },
-        body: function() {
-            var $form = $(document.createElement('form'));
-            var $fieldset = $(document.createElement('fieldset'));
-            // static route name
-            var $label = one.lib.form.label("Name");
-            var $input = one.lib.form.input("Name");
-            $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.routeName);
-            $fieldset.append($label).append($input);
-            // static route IP Mask 
-            var $label = one.lib.form.label("Static Route");
-            var $input = one.lib.form.input("Static Route");
-            var $help = one.lib.form.help('Example: 53.55.0.0/16');
-            $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute);
-            $fieldset.append($label).append($input).append($help);
-            // static route IP Mask 
-            var $label = one.lib.form.label("Next Hop");
-            var $input = one.lib.form.input("Next Hop");
-            var $help = one.lib.form.help('Example: 192.168.10.254');
-            $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop);
-            $fieldset.append($label).append($input).append($help);
-            // return
-            $form.append($fieldset);
-            return $form;
-        },
-        ajax: {
-            staticRouteConfig: function(requestData, callback) {
-                $.getJSON(one.f.switchmanager.rootUrl + "/staticRoute/add", requestData, function(data) {
-                    callback(data);
-                });
-            }
-        },
-        data : {
-            
-        },
-        footer : function() {
-            var footer = [];
-            var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.staticRouteConfig.id.modal.save, "btn-primary", "");
-            var $saveButton = one.lib.dashlet.button.button(saveButton);
-            footer.push($saveButton);
-            return footer;
+          }
+        });
+        $modal.modal();
+      },
+      footer : function() {
+        var footer = [];
+        var remove = one.lib.dashlet.button.single('Remove Static Route',one.f.switchmanager.staticRouteConfig.id.modal.remove, 'btn-danger', '');
+        var $remove = one.lib.dashlet.button.button(remove);
+        footer.push($remove);
+
+        var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.staticRouteConfig.id.modal.cancel, '', '');
+        var $cancel = one.lib.dashlet.button.button(cancel);
+        footer.push($cancel);
+
+        return footer;
+      },
+      body : function (staticRouteList) {
+        var $p = $(document.createElement('p'));
+        var p = 'Remove the following Static Route(s)?';
+        //creata a BS label for each rule and append to list
+        $(staticRouteList).each(function(){
+          var $span = $(document.createElement('span'));
+          $span.append(this);
+          p += '<br/>' + $span[0].outerHTML;
+        });
+        $p.append(p);
+        return $p;
+      }
+    }
+  },
+  // data functions
+  data : {
+    staticRouteConfigGrid: function(data) {
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'selector',
+      label: "<input type='checkbox'  id='"
+        +one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll+"'/>",
+      sortable: false
+      },
+      {
+        property: 'name',
+      label: 'Name',
+      sortable: true
+      },
+      {
+        property: 'staticRoute',
+      label: 'Static Route',
+      sortable: true
+      },
+      {
+        property: 'nextHop',
+          label: 'Next Hop Address',
+          sortable: true
+      }
+      ],
+        data: data.nodeData,
+        formatter: function(items) {
+          $.each(items, function(index, item) {
+            item["selector"] = '<input type="checkbox" class="staticRoute" id="' + item["name"] + '"></input>';
+          });
+
         },
-        removeMultiple: {
-            dialog: function(routesToDelete) {
-                var h3 = 'Remove Static Route';
-                
-                var footer = one.f.switchmanager.staticRouteConfig.modal.removeMultiple.footer();
-                var $body = one.f.switchmanager.staticRouteConfig.modal.removeMultiple.body(routesToDelete);
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.staticRouteConfig.id.modal.modal, h3, $body, footer);
-
-                // bind close button
-                $('#'+one.f.switchmanager.staticRouteConfig.id.modal.cancel, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-
-                // bind remove rule button
-                $('#'+one.f.switchmanager.staticRouteConfig.id.modal.remove, $modal).click(this, function(e) {
-                    if (routesToDelete.length > 0) {
-                        var requestData = {};
-                        requestData["routesToDelete"] = routesToDelete.toString();
-                        var url = one.f.switchmanager.rootUrl + "/staticRoute/delete";
-                        one.f.switchmanager.staticRouteConfig.ajax.main(url, requestData, function(response) {
-                            $modal.modal('hide');
-                            if (response.status == true) {
-                                // refresh dashlet by passing dashlet div as param 
-                                one.lib.alert("Static Route(s) successfully removed");
-                            } else {
-                                alert(response.message);
-                            }
-                            one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
-                        });
-                    }
-                });
-                $modal.modal();
-            },
-            footer : function() {
-                var footer = [];
-                var remove = one.lib.dashlet.button.single('Remove Static Route',one.f.switchmanager.staticRouteConfig.id.modal.remove, 'btn-danger', '');
-                var $remove = one.lib.dashlet.button.button(remove);
-                footer.push($remove);
-
-                var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.staticRouteConfig.id.modal.cancel, '', '');
-                var $cancel = one.lib.dashlet.button.button(cancel);
-                footer.push($cancel);
-
-                return footer;
-            },
-            body : function (staticRouteList) {
-                var $p = $(document.createElement('p'));
-                var p = 'Remove the following Static Route(s)?';
-                //creata a BS label for each rule and append to list
-                $(staticRouteList).each(function(){
-                    var $span = $(document.createElement('span'));
-                    $span.append(this);
-                    p += '<br/>' + $span[0].outerHTML;
-                });
-                $p.append(p);
-                return $p;
-            }
-        }
+        delay: 0
+      });
+      return source;              
     },
-    // data functions
-    data : {
-        staticRouteConfigGrid: function(data) {
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'selector',
-                            label: "<input type='checkbox'  id='"
-                                +one.f.switchmanager.staticRouteConfig.id.dashlet.selectAll+"'/>",
-                            sortable: false
-                        },
-                        {
-                            property: 'name',
-                            label: 'Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'staticRoute',
-                            label: 'Static Route',
-                            sortable: true
-                        },
-                        {
-                            property: 'nextHop',
-                            label: 'Next Hop Address',
-                            sortable: true
-                        }
-                    ],
-                    data: data.nodeData,
-                    formatter: function(items) {
-                        $.each(items, function(index, item) {
-                            item["selector"] = '<input type="checkbox" class="staticRoute" id="' + item["name"] + '"></input>';
-                        });
-
-                    },
-                    delay: 0
-                });
-            return source;              
-        },
-        staticRouteConfig : function(data) {
-            var result = [];
-            $.each(data.nodeData, function(key, value) {
-                var tr = {};
-                // fill up all the td's
-                var entry = [];
-                var checkbox = document.createElement("input");
-                checkbox.setAttribute("type", "checkbox");
-                checkbox.setAttribute("id", value["name"]);
-                entry.push(checkbox);
-                entry.push(value["name"]);
-                entry.push(value["staticRoute"]);
-                entry.push(value["nextHop"]);
-                tr.entry = entry;
-                result.push(tr);
-            });
-            return result;
-        }
+    staticRouteConfig : function(data) {
+      var result = [];
+      $.each(data.nodeData, function(key, value) {
+        var tr = {};
+        // fill up all the td's
+        var entry = [];
+        var checkbox = document.createElement("input");
+        checkbox.setAttribute("type", "checkbox");
+        checkbox.setAttribute("id", value["name"]);
+        entry.push(checkbox);
+        entry.push(value["name"]);
+        entry.push(value["staticRoute"]);
+        entry.push(value["nextHop"]);
+        tr.entry = entry;
+        result.push(tr);
+      });
+      return result;
     }
+  }
 }
 
 one.f.switchmanager.spanPortConfig = {
-    id: {
-        dashlet: {
-            add: "one_f_switchmanager_spanPortConfig_id_dashlet_add",
-            remove: "one_f_switchmanager_spanPortConfig_id_dashlet_remove",
-            datagrid: "one_f_switchmanager_spanPortConfig_id_dashlet_datagrid",
-            selectAllFlows: "one_f_switchmanager_spanPortConfig_id_dashlet_selectAllFlows"
-        }, 
-        modal: {
-            modal: "one_f_switchmanager_spanPortConfig_id_modal_modal",
-            save: "one_f_switchmanager_spanPortConfig_id_modal_save",
-            cancel: "one_f_switchmanager_spanPortConfig_id_modal_cancel",
-            remove: "one_f_switchmanager_spanPortConfig_id_modal_remove",
-            form: {
-                name : "one_f_switchmanager_spanPortConfig_id_modal_form_name",
-                nodes : "one_f_switchmanager_spanPortConfig_id_modal_form_nodes",
-                port : "one_f_switchmanager_spanPortConfig_id_modal_form_port",
+  id: {
+    dashlet: {
+      add: "one_f_switchmanager_spanPortConfig_id_dashlet_add",
+      remove: "one_f_switchmanager_spanPortConfig_id_dashlet_remove",
+      datagrid: "one_f_switchmanager_spanPortConfig_id_dashlet_datagrid",
+      selectAllFlows: "one_f_switchmanager_spanPortConfig_id_dashlet_selectAllFlows"
+    }, 
+    modal: {
+      modal: "one_f_switchmanager_spanPortConfig_id_modal_modal",
+      save: "one_f_switchmanager_spanPortConfig_id_modal_save",
+      cancel: "one_f_switchmanager_spanPortConfig_id_modal_cancel",
+      remove: "one_f_switchmanager_spanPortConfig_id_modal_remove",
+      form: {
+        name : "one_f_switchmanager_spanPortConfig_id_modal_form_name",
+        nodes : "one_f_switchmanager_spanPortConfig_id_modal_form_nodes",
+        port : "one_f_switchmanager_spanPortConfig_id_modal_form_port",
+      }
+    }
+  },
+  dashlet: function($dashlet) {
+    one.lib.dashlet.empty($dashlet);
+
+    //populate table in dashlet
+    var url = one.f.switchmanager.rootUrl + "/spanPorts";
+    one.f.switchmanager.spanPortConfig.ajax.main(url, {}, function(content) {
+
+      if (content.privilege === 'WRITE') {
+
+        // Add span port button
+        var button = one.lib.dashlet.button.single("Add SPAN Port", one.f.switchmanager.spanPortConfig.id.dashlet.add, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+
+        $button.click(function() {
+          var $modal = one.f.switchmanager.spanPortConfig.modal.initialize();
+          $modal.modal();
+        });
+        $dashlet.append(one.lib.dashlet.header(one.f.dashlet.spanPortConfig.name));
+        $dashlet.append($button);
+
+        // Delete span port button
+        var button = one.lib.dashlet.button.single("Remove SPAN Port", one.f.switchmanager.spanPortConfig.id.dashlet.remove, "btn-danger", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+        $button.click(function() {
+          var spanPortsToDelete = [];
+          var checkedCheckBoxes = $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).find("tbody input:checked");
+
+          if (checkedCheckBoxes.size() === 0) {
+            return false;
+          }
+          checkedCheckBoxes.each(function(index, value) {
+            var spanPortObj = {};
+            spanPortObj['spanPortJson'] = decodeURIComponent(checkedCheckBoxes[index].getAttribute("spanPort"));
+            spanPortObj['spanPortNodeName'] = checkedCheckBoxes[index].getAttribute("spanPortNode");
+            spanPortObj['spanPortPortName'] = checkedCheckBoxes[index].getAttribute("spanPortPort");
+
+            spanPortsToDelete.push(spanPortObj);
+          });
+          one.f.switchmanager.spanPortConfig.modal.removeMultiple.dialog(spanPortsToDelete);
+        });
+        $dashlet.append($button);
+      }
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.spanPortConfig.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.switchmanager.spanPortConfig.data.spanPortConfigGrid(content);
+      $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
+        .on("loaded", function() {
+          $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll).click(function() {
+            $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
+              $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll).is(':checked'));
+          });
+          $(".spanPortConfig").click(function(e){
+            if (!$('.spanPortConfig[type=checkbox]:not(:checked)').length) {
+              $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll)
+            .prop("checked",
+              true);
+            } else {
+              $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll)
+            .prop("checked",
+              false);
             }
-        }
+            e.stopPropagation();
+          });
+        });
+    });
+  },
+  // device ajax calls
+  ajax : {
+    main : function(url, requestData, callback) {
+      $.getJSON(url, requestData, function(data) {
+        callback(data);
+      });
+    }
+  },
+  registry: {},
+  modal : {
+    initialize: function() {
+      var h3 = "Add SPAN Port";
+      var footer = one.f.switchmanager.spanPortConfig.modal.footer();
+      var $modal = one.lib.modal.spawn(one.f.switchmanager.spanPortConfig.id.modal.modal, h3, "", footer);
+      // bind save button
+      $('#' + one.f.switchmanager.spanPortConfig.id.modal.save, $modal).click(function() {
+        one.f.switchmanager.spanPortConfig.modal.save($modal);
+      });
+
+      one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
+        var $body = one.f.switchmanager.spanPortConfig.modal.body(nodes, nodeports);
+        one.lib.modal.inject.body($modal, $body);
+      });
+      return $modal;
     },
-    dashlet: function($dashlet) {
-        one.lib.dashlet.empty($dashlet);
-        
-        //populate table in dashlet
-        var url = one.f.switchmanager.rootUrl + "/spanPorts";
-        one.f.switchmanager.spanPortConfig.ajax.main(url, {}, function(content) {
-
-            if (content.privilege === 'WRITE') {
-
-                // Add span port button
-                var button = one.lib.dashlet.button.single("Add SPAN Port", one.f.switchmanager.spanPortConfig.id.dashlet.add, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-
-                $button.click(function() {
-                    var $modal = one.f.switchmanager.spanPortConfig.modal.initialize();
-                    $modal.modal();
-                });
-                $dashlet.append(one.lib.dashlet.header(one.f.dashlet.spanPortConfig.name));
-                $dashlet.append($button);
-
-                // Delete span port button
-                var button = one.lib.dashlet.button.single("Remove SPAN Port", one.f.switchmanager.spanPortConfig.id.dashlet.remove, "btn-danger", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-                $button.click(function() {
-                    var spanPortsToDelete = [];
-                    var checkedCheckBoxes = $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).find("tbody input:checked");
-                    
-                    if (checkedCheckBoxes.size() === 0) {
-                        return false;
-                    }
-                    checkedCheckBoxes.each(function(index, value) {
-                        var spanPortObj = {};
-                        spanPortObj['spanPortJson'] = decodeURIComponent(checkedCheckBoxes[index].getAttribute("spanPort"));
-                        spanPortObj['spanPortNodeName'] = checkedCheckBoxes[index].getAttribute("spanPortNode");
-                        spanPortObj['spanPortPortName'] = checkedCheckBoxes[index].getAttribute("spanPortPort");
-
-                        spanPortsToDelete.push(spanPortObj);
-                    });
-                    one.f.switchmanager.spanPortConfig.modal.removeMultiple.dialog(spanPortsToDelete);
-                });
-                $dashlet.append($button);
+    save: function($modal) {
+      var result = {};
+      result['nodeId'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.nodes, $modal).val();
+      result['spanPort'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port, $modal).val();
+      one.f.switchmanager.spanPortConfig.modal.ajax.saveSpanPortConfig(result, 
+          function(response) {
+            if(response.status == true) {
+              $modal.modal('hide');
+              one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
+            } else {
+              alert(response.message);
             }
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.spanPortConfig.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.switchmanager.spanPortConfig.data.spanPortConfigGrid(content);
-            $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource})
-            .on("loaded", function() {
-                $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll).click(function() {
-                    $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).find(':checkbox').prop('checked',
-                        $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll).is(':checked'));
-                });
-                $(".spanPortConfig").click(function(e){
-                    if (!$('.spanPortConfig[type=checkbox]:not(:checked)').length) {
-                        $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                          true);
-                    } else {
-                        $("#"+one.f.switchmanager.spanPortConfig.id.dashlet.selectAll)
-                            .prop("checked",
-                         false);
-                    }
-                    e.stopPropagation();
-                });
-             });
+
+          });
+    },
+    body: function(nodes, nodeports) {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      // node
+      var $label = one.lib.form.label("Node");
+      var $select = one.lib.form.select.create(nodes);
+      one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+      $select.val($select.find("option:first").val());
+      $select.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.nodes);
+
+      // bind onchange
+      $select.change(function() {
+        // retrieve port value
+        var nodeId = $(this).find('option:selected').attr('value');
+        one.f.switchmanager.spanPortConfig.registry['currentNode'] = nodeId;
+        var $ports = $('#'+one.f.switchmanager.spanPortConfig.id.modal.form.port);
+        var ports = one.f.switchmanager.spanPortConfig.registry['nodePorts'][nodeId]
+        var options = {};
+      $(ports).each(function(idx, val) {
+        options[val.internalPortName] = val.portName+' ('+val.portId+')'; 
+          });
+        one.lib.form.select.inject($ports, options); 
+        one.lib.form.select.prepend($ports, {'':'Please Select a Port'});
+        $ports.val($ports.find('option:first').val());
+        });
+
+      $fieldset.append($label).append($select);
+      // input port
+      var $label = one.lib.form.label("Input Port");
+      var $select = one.lib.form.select.create();
+      one.lib.form.select.prepend($select, {'':'None'});
+      $select.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.port);
+      $select.val($select.find('option:first').val());
+      $fieldset.append($label).append($select);
+
+      // return
+      $form.append($fieldset);
+      return $form;
+    },
+    ajax: {
+      nodes: function(callback) {
+        $.getJSON(one.f.switchmanager.rootUrl + "/nodeports", function(data) {
+          var nodes = {};
+          var nodePorts = {};
+          $(data).each(function(index, node) {
+            nodes[node.nodeId] = node.nodeName;
+            nodePorts[node.nodeId] = node.nodePorts;
+          });
+          one.f.switchmanager.spanPortConfig.registry['nodePorts'] = nodePorts;
+          callback(nodes, nodePorts);
         });
+      },
+      saveSpanPortConfig: function(requestData, callback) {
+        var resource = {};
+        resource["jsonData"] = JSON.stringify(requestData);
+        $.getJSON(one.f.switchmanager.rootUrl + "/spanPorts/add", resource, function(data) {
+          callback(data);
+        });
+      }
     },
-    // device ajax calls
-    ajax : {
-        main : function(url, requestData, callback) {
-            $.getJSON(url, requestData, function(data) {
-                callback(data);
-            });
-        }
+    footer : function() {
+      var footer = [];
+      var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.spanPortConfig.id.modal.save, "btn-primary", "");
+      var $saveButton = one.lib.dashlet.button.button(saveButton);
+      footer.push($saveButton);
+      return footer;
     },
-    registry: {},
-    modal : {
-        initialize: function() {
-            var h3 = "Add SPAN Port";
-            var footer = one.f.switchmanager.spanPortConfig.modal.footer();
-            var $modal = one.lib.modal.spawn(one.f.switchmanager.spanPortConfig.id.modal.modal, h3, "", footer);
-            // bind save button
-            $('#' + one.f.switchmanager.spanPortConfig.id.modal.save, $modal).click(function() {
-                one.f.switchmanager.spanPortConfig.modal.save($modal);
-            });
+    removeMultiple: {
+      dialog: function(spanPortsToDelete) {
+        var h3 = 'Remove SPAN Port';
 
-            one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
-                var $body = one.f.switchmanager.spanPortConfig.modal.body(nodes, nodeports);
-                one.lib.modal.inject.body($modal, $body);
-            });
-            return $modal;
-        },
-        save: function($modal) {
-            var result = {};
-            result['nodeId'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.nodes, $modal).val();
-            result['spanPort'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port, $modal).val();
-            one.f.switchmanager.spanPortConfig.modal.ajax.saveSpanPortConfig(result, 
-                function(response) {
-                    if(response.status == true) {
-                        $modal.modal('hide');
-                        one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
-                    } else {
-                        alert(response.message);
-                    }
-                    
-                });
-        },
-        body: function(nodes, nodeports) {
-            var $form = $(document.createElement('form'));
-            var $fieldset = $(document.createElement('fieldset'));
-            // node
-            var $label = one.lib.form.label("Node");
-            var $select = one.lib.form.select.create(nodes);
-            one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
-            $select.val($select.find("option:first").val());
-            $select.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.nodes);
-            
-            // bind onchange
-            $select.change(function() {
-                // retrieve port value
-                var nodeId = $(this).find('option:selected').attr('value');
-                one.f.switchmanager.spanPortConfig.registry['currentNode'] = nodeId;
-                var $ports = $('#'+one.f.switchmanager.spanPortConfig.id.modal.form.port);
-                var ports = one.f.switchmanager.spanPortConfig.registry['nodePorts'][nodeId]
-                var options = {};
-                $(ports).each(function(idx, val) {
-                    options[val.internalPortName] = val.portName+' ('+val.portId+')'; 
-                });
-                one.lib.form.select.inject($ports, options); 
-                one.lib.form.select.prepend($ports, {'':'Please Select a Port'});
-                $ports.val($ports.find('option:first').val());
-            });
+        var footer = one.f.switchmanager.spanPortConfig.modal.removeMultiple.footer();
+        var $body = one.f.switchmanager.spanPortConfig.modal.removeMultiple.body(spanPortsToDelete);
+        var $modal = one.lib.modal.spawn(one.f.switchmanager.spanPortConfig.id.modal.modal, h3, $body, footer);
 
-            $fieldset.append($label).append($select);
-            // input port
-            var $label = one.lib.form.label("Input Port");
-            var $select = one.lib.form.select.create();
-            one.lib.form.select.prepend($select, {'':'None'});
-            $select.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.port);
-            $select.val($select.find('option:first').val());
-            $fieldset.append($label).append($select);
-            
-            // return
-            $form.append($fieldset);
-            return $form;
-        },
-        ajax: {
-            nodes: function(callback) {
-                $.getJSON(one.f.switchmanager.rootUrl + "/nodeports", function(data) {
-                    var nodes = {};
-                    var nodePorts = {};
-                    $(data).each(function(index, node) {
-                        nodes[node.nodeId] = node.nodeName;
-                        nodePorts[node.nodeId] = node.nodePorts;
-                    });
-                    one.f.switchmanager.spanPortConfig.registry['nodePorts'] = nodePorts;
-                    callback(nodes, nodePorts);
-                });
-            },
-            saveSpanPortConfig: function(requestData, callback) {
-                var resource = {};
-                resource["jsonData"] = JSON.stringify(requestData);
-                $.getJSON(one.f.switchmanager.rootUrl + "/spanPorts/add", resource, function(data) {
-                    callback(data);
-                });
-            }
-        },
-        footer : function() {
-            var footer = [];
-            var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.spanPortConfig.id.modal.save, "btn-primary", "");
-            var $saveButton = one.lib.dashlet.button.button(saveButton);
-            footer.push($saveButton);
-            return footer;
-        },
-        removeMultiple: {
-            dialog: function(spanPortsToDelete) {
-                var h3 = 'Remove SPAN Port';
-                
-                var footer = one.f.switchmanager.spanPortConfig.modal.removeMultiple.footer();
-                var $body = one.f.switchmanager.spanPortConfig.modal.removeMultiple.body(spanPortsToDelete);
-                var $modal = one.lib.modal.spawn(one.f.switchmanager.spanPortConfig.id.modal.modal, h3, $body, footer);
-
-                // bind close button
-                $('#'+one.f.switchmanager.spanPortConfig.id.modal.cancel, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-
-                // bind remove rule button
-                $('#'+one.f.switchmanager.spanPortConfig.id.modal.remove, $modal).click(this, function(e) {
-                    var requestData = {};
-                    var spanPorts = [];
-                    $(spanPortsToDelete).each(function(index, spanPort) {
-                        spanPorts.push(JSON.parse(spanPort.spanPortJson));
-                    });
-                    requestData["spanPortsToDelete"] = JSON.stringify(spanPorts);
-
-                    var url = one.f.switchmanager.rootUrl + "/spanPorts/delete";
-                    one.f.switchmanager.spanPortConfig.ajax.main(url, requestData, function(response) {
-                        $modal.modal('hide');
-                        if (response.status == true) {
-                            // refresh dashlet by passing dashlet div as param
-                            one.lib.alert("Span Port(s) successfully removed");
-                        } else {
-                            alert(response.message);
-                        }
-                        one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
-                    });
-                });
-                $modal.modal();
-            },
-            footer : function() {
-                var footer = [];
-                var remove = one.lib.dashlet.button.single('Remove SPAN Port',one.f.switchmanager.spanPortConfig.id.modal.remove, 'btn-danger', '');
-                var $remove = one.lib.dashlet.button.button(remove);
-                footer.push($remove);
-
-                var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.spanPortConfig.id.modal.cancel, '', '');
-                var $cancel = one.lib.dashlet.button.button(cancel);
-                footer.push($cancel);
-
-                return footer;
-            },
-            body : function (spanPortToDelete) {
-                var $p = $(document.createElement('p'));
-                var p = 'Remove the following Span Port(s)?';
-                //creata a BS label for each rule and append to list
-
-                $(spanPortToDelete).each(function(index, spanPortItem) {
-                    var $span = $(document.createElement('span'));
-                    $span.append(this.spanPortNodeName+"-"+this.spanPortPortName);
-                    p += '<br/>' + $span[0].outerHTML;
-                });
-                $p.append(p);
-                return $p;
+        // bind close button
+        $('#'+one.f.switchmanager.spanPortConfig.id.modal.cancel, $modal).click(function() {
+          $modal.modal('hide');
+        });
+
+        // bind remove rule button
+        $('#'+one.f.switchmanager.spanPortConfig.id.modal.remove, $modal).click(this, function(e) {
+          var requestData = {};
+          var spanPorts = [];
+          $(spanPortsToDelete).each(function(index, spanPort) {
+            spanPorts.push(JSON.parse(spanPort.spanPortJson));
+          });
+          requestData["spanPortsToDelete"] = JSON.stringify(spanPorts);
+
+          var url = one.f.switchmanager.rootUrl + "/spanPorts/delete";
+          one.f.switchmanager.spanPortConfig.ajax.main(url, requestData, function(response) {
+            $modal.modal('hide');
+            if (response.status == true) {
+              // refresh dashlet by passing dashlet div as param
+              one.lib.alert("Span Port(s) successfully removed");
+            } else {
+              alert(response.message);
             }
-        }
+            one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
+          });
+        });
+        $modal.modal();
+      },
+      footer : function() {
+        var footer = [];
+        var remove = one.lib.dashlet.button.single('Remove SPAN Port',one.f.switchmanager.spanPortConfig.id.modal.remove, 'btn-danger', '');
+        var $remove = one.lib.dashlet.button.button(remove);
+        footer.push($remove);
+
+        var cancel = one.lib.dashlet.button.single('Cancel', one.f.switchmanager.spanPortConfig.id.modal.cancel, '', '');
+        var $cancel = one.lib.dashlet.button.button(cancel);
+        footer.push($cancel);
+
+        return footer;
+      },
+      body : function (spanPortToDelete) {
+        var $p = $(document.createElement('p'));
+        var p = 'Remove the following Span Port(s)?';
+        //creata a BS label for each rule and append to list
+
+        $(spanPortToDelete).each(function(index, spanPortItem) {
+          var $span = $(document.createElement('span'));
+          $span.append(this.spanPortNodeName+"-"+this.spanPortPortName);
+          p += '<br/>' + $span[0].outerHTML;
+        });
+        $p.append(p);
+        return $p;
+      }
+    }
+  },
+  // data functions
+  data : {
+    spanPortConfigGrid: function(data) {
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'selector',
+      label: "<input type='checkbox'  id='"
+        +one.f.switchmanager.spanPortConfig.id.dashlet.selectAll+"'/>",
+      sortable: false
+      },
+      {
+        property: 'nodeName',
+      label: 'Node',
+      sortable: true
+      },
+      {
+        property: 'spanPortName',
+      label: 'SPAN Port',
+      sortable: true
+      },
+      ],
+      data: data.nodeData,
+      formatter: function(items) {
+        $.each(items, function(index, item) {
+          item["selector"] = '<input type="checkbox" class="spanPortConfig" spanPort=' + encodeURIComponent(item["json"]) + ' spanPortNode="' + item["nodeName"] + '" spanPortPort="' + item["spanPortName"] + '"></input>';
+        });
+      },
+      delay: 0
+      });
+      return source;              
     },
-    // data functions
-    data : {
-        spanPortConfigGrid: function(data) {
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'selector',
-                            label: "<input type='checkbox'  id='"
-                                +one.f.switchmanager.spanPortConfig.id.dashlet.selectAll+"'/>",
-                            sortable: false
-                        },
-                        {
-                            property: 'nodeName',
-                            label: 'Node',
-                            sortable: true
-                        },
-                        {
-                            property: 'spanPortName',
-                            label: 'SPAN Port',
-                            sortable: true
-                        },
-                    ],
-                    data: data.nodeData,
-                    formatter: function(items) {
-                        $.each(items, function(index, item) {
-                            item["selector"] = '<input type="checkbox" class="spanPortConfig" spanPort=' + encodeURIComponent(item["json"]) + ' spanPortNode="' + item["nodeName"] + '" spanPortPort="' + item["spanPortName"] + '"></input>';
-                        });
-                    },
-                    delay: 0
-                });
-            return source;              
-        },
-        devices : function(data) {
-            var result = [];
-            $.each(data.nodeData, function(key, value) {
-                var tr = {};
-                // fill up all the td's
-                var entry = [];
-                var checkbox = document.createElement("input");
-                checkbox.setAttribute("type", "checkbox");
-                checkbox.spanPort = value.json;
-                entry.push(checkbox);
-                entry.push(value["nodeName"]);
-                entry.push(value["spanPort"]);
-                tr.entry = entry;
-                result.push(tr);
-            });
-            return result;
+    devices : function(data) {
+      var result = [];
+      $.each(data.nodeData, function(key, value) {
+        var tr = {};
+        // fill up all the td's
+        var entry = [];
+        var checkbox = document.createElement("input");
+        checkbox.setAttribute("type", "checkbox");
+        checkbox.spanPort = value.json;
+        entry.push(checkbox);
+        entry.push(value["nodeName"]);
+        entry.push(value["spanPort"]);
+        tr.entry = entry;
+        result.push(tr);
+      });
+      return result;
+    }
+  }
+}
+
+one.f.connection = {
+  id : { // one.f.connection.id
+    datagrid : 'one-f-connection-id-datagrid',
+    add : 'one-f-connection-id-add'
+  },
+  dashlet: function($dashlet) {
+    one.lib.dashlet.empty($dashlet);
+    // heading
+    $dashlet.append(one.lib.dashlet.header(one.f.dashlet.connection.name));
+    // add button
+    var add = one.lib.dashlet.button.single('Add Node', one.f.connection.id.add, 'btn-primary', 'btn-mini');
+    var $add = one.lib.dashlet.button.button(add);
+    $add.click(function() {
+      one.f.connection.add.initialize();
+    });
+    $dashlet.append($add);
+    // load table
+    var url = one.f.switchmanager.rootUrl+'/connect/nodes';
+    $.getJSON(url, function(data) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.connection.id.datagrid, {
+        searchable: true,
+        filterable: false,
+        pagination: true,
+        flexibleRowsPerPage: true
+      }, 'table-striped table-condensed table-cursor');
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.connection.data(data);
+      $('#'+one.f.connection.id.datagrid, $dashlet).datagrid({dataSource:dataSource})
+        .on('loaded', function() {
+          $(this).find('tbody tr').click(function() {
+            var nodeId = $(this).find('.nodeId').text();
+            var nodeType = $(this).find('.nodeType').text();
+            var nodeName = $(this).find('.nodeName').text();
+            one.f.connection.remove.initialize(nodeId, nodeName, nodeType);
+          });
+        });
+    });
+  },
+  data : function(data) {
+    var source = new StaticDataSource({
+      columns: [
+      {
+        property: 'nodeName',
+        label: 'Node',
+        sortable: true
+      }
+      ],
+      data: data,
+      formatter: function(items) {
+        $.each(items, function(index, item) {
+          var $nodeId = $(document.createElement('span'));
+          $nodeId.css('display', 'none');
+          var $nodeType = $nodeId.clone();
+          var $nodeName = $nodeId.clone();
+          $nodeId.append(item.nodeId).addClass('nodeId');
+          $nodeType.append(item.nodeType).addClass('nodeType');
+          $nodeName.append(item.nodeName).addClass('nodeName');
+          item.nodeName += $nodeId[0].outerHTML+$nodeType[0].outerHTML+$nodeName[0].outerHTML;
+        });
+      },
+      delay: 0
+    });
+    return source;              
+  },
+  add : {
+    id : { // one.f.connection.add.id
+      modal : 'one-f-connection-add-id-modal',
+      add : 'one-f-connection-add-id-add',
+      cancel : 'one-f-connection-add-id-cancel',
+      form : {
+        nodeId : 'one-f-connection-add-id-form-nodeId',
+        ipAddress : 'one-f-connection-add-id-form-ipAddress',
+        port : 'one-f-connection-add-id-form-port',
+        nodeType : 'one-f-connection-add-id-form-nodeType'
+      }
+    },
+    initialize : function() {
+      var h3 = 'Add Node';
+      var footer = one.f.connection.add.footer();
+      var $body = one.f.connection.add.body();;
+      var $modal = one.lib.modal.spawn(one.f.connection.add.id.modal, h3, $body, footer);
+      // bind add buton
+      $('#'+one.f.connection.add.id.add, $modal).click(function() {
+        var nodeId = $('#'+one.f.connection.add.id.form.nodeId, $modal).val();
+        if (nodeId === '') {
+          alert('Please enter a node ID');
+          return false;
+        }
+        var resources = {};
+        resources.ipAddress = $('#'+one.f.connection.add.id.form.ipAddress, $modal).val();
+        if (resources.ipAddress === '') {
+          alert('Please enter an IP Address');
+          return false;
+        }
+        resources.port = $('#'+one.f.connection.add.id.form.port, $modal).val();
+        if (resources.port === '') {
+          alert('Please enter a port');
+          return false;
+        }
+        var nodeType = $('#'+one.f.connection.add.id.form.nodeType, $modal).val();
+        if (nodeType !== '') {
+          resources.nodeType = nodeType;
         }
+        var url = one.f.switchmanager.rootUrl+'/connect/'+encodeURI(nodeId);
+        $.post(url, resources, function(result) {
+          if (result.success === true) {
+            $modal.modal('hide');
+            one.lib.alert(result.description);
+          } else {
+            alert(result.code+': '+result.description);
+          }
+        });
+      });
+      // bind cancel button
+      $('#'+one.f.connection.add.id.cancel, $modal).click(function() {
+        $modal.modal('hide');
+      });
+      $modal.modal();
+    },
+    body : function() {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      // node id
+      var $label = one.lib.form.label('Node ID');
+      var $input = one.lib.form.input('Node ID');
+      $input.attr('id', one.f.connection.add.id.form.nodeId);
+      $fieldset.append($label).append($input);
+      // ip address
+      $label = one.lib.form.label('IP Address');
+      $input = one.lib.form.input('IP Address');
+      $input.attr('id', one.f.connection.add.id.form.ipAddress);
+      $fieldset.append($label).append($input);
+      // port
+      $label = one.lib.form.label('Port');
+      $input = one.lib.form.input('Port');
+      $input.attr('id', one.f.connection.add.id.form.port);
+      var $help = one.lib.form.help('Enter a number');
+      $fieldset.append($label).append($input).append($help);
+      // node type
+      $label = one.lib.form.label('Node Type');
+      $input = one.lib.form.input('Node Type');
+      $input.attr('id', one.f.connection.add.id.form.nodeType);
+      $help = one.lib.form.help('Optional');
+      $fieldset.append($label).append($input).append($help);
+      $form.append($fieldset);
+      return $form;
+    },
+    footer : function() {
+      var footer = [];
+      var add = one.lib.dashlet.button.single('Submit', one.f.connection.add.id.add, 'btn-primary', '');
+      var $add = one.lib.dashlet.button.button(add);
+      footer.push($add);
+      var cancel = one.lib.dashlet.button.single('Cancel', one.f.connection.add.id.cancel, '', '');
+      var $cancel = one.lib.dashlet.button.button(cancel);
+      footer.push($cancel);
+      return footer;
     }
+  },
+  remove : {
+    id : { // one.f.connection.remove.id
+      modal : 'one-f-connection-remove-id-modal',
+      remove : 'one-f-connection-remove-id-remove',
+      cancel : 'one-f-connection-remove-id-cancel'
+    },
+    initialize : function(nodeId, nodeName, nodeType) {
+      var h3 = 'Remove Node';
+      var footer = one.f.connection.remove.footer();
+      var $body = one.f.connection.remove.body(nodeName);
+      var $modal = one.lib.modal.spawn(one.f.connection.remove.id.modal, h3, $body, footer);
+      // bind remove buton
+      $('#'+one.f.connection.remove.id.remove, $modal).click(function() {
+        var resources = {};
+        resources.nodeType = nodeType;
+        var url = one.f.switchmanager.rootUrl+'/disconnect/'+encodeURI(nodeId);
+        $.post(url, resources, function(result) {
+          if (result.success === true) {
+            $modal.modal('hide');
+            one.lib.alert(result.description);
+          } else {
+            alert(result.code+': '+result.description);
+          }
+        }).fail(function() { debugger; });
+      });
+      // bind cancel button
+      $('#'+one.f.connection.remove.id.cancel, $modal).click(function() {
+        $modal.modal('hide');
+      });
+      $modal.modal();
+    },
+    body : function(nodeName) {
+      var $p = $(document.createElement('p'));
+      $p.append('Remove the following node? ');
+      var $span = $(document.createElement('span'));
+      $span.addClass('label label-info');
+      $span.append(nodeName);
+      $p.append($span[0].outerHTML);
+      return $p;
+    },
+    footer : function() {
+      var footer = [];
+      var remove = one.lib.dashlet.button.single('Remove', one.f.connection.remove.id.remove, 'btn-danger', '');
+      var $remove = one.lib.dashlet.button.button(remove);
+      footer.push($remove);
+      var cancel = one.lib.dashlet.button.single('Cancel', one.f.connection.remove.id.cancel, '', '');
+      var $cancel = one.lib.dashlet.button.button(cancel);
+      footer.push($cancel);
+      return footer;
+    }
+  }
 }
 
 /** INIT **/
 // populate nav tabs
 $(one.f.menu.left.top).each(function(index, value) {
-    var $nav = $(".nav", "#left-top");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#left-top");
+  one.main.page.dashlet($nav, value);
 });
 
 $(one.f.menu.left.bottom).each(function(index, value) {
-    var $nav = $(".nav", "#left-bottom");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#left-bottom");
+  one.main.page.dashlet($nav, value);
 });
 
 $(one.f.menu.right.bottom).each(function(index, value) {
-    var $nav = $(".nav", "#right-bottom");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#right-bottom");
+  one.main.page.dashlet($nav, value);
 });
 
 one.f.addPopOut = function() {
-    $img1 = $(document.createElement("img"));
-    $img1.attr("src", "/img/Expand16T.png");
-    $img1.attr("style", "float: right;");
-    $img1.hover(function() {
-        $img1.css("cursor", "pointer");
+  $img1 = $(document.createElement("img"));
+  $img1.attr("src", "/img/Expand16T.png");
+  $img1.attr("style", "float: right;");
+  $img1.hover(function() {
+    $img1.css("cursor", "pointer");
+  });
+  $img1.click(function() {
+    var $modal = one.f.switchmanager.nodesLearnt.modal.initialize.popout();
+    $modal.css({
+      'margin-left': '-45%',
+      'margin-top': '-3%',
+      'width':$(document).width() * 0.8,
+      'height':$(document).height() * 0.9
     });
-    $img1.click(function() {
-        var $modal = one.f.switchmanager.nodesLearnt.modal.initialize.popout();
-        $modal.css({
-            'margin-left': '-45%',
-            'margin-top': '-3%',
-            'width':$(document).width() * 0.8,
-            'height':$(document).height() * 0.9
-        });
-        $(".modal-body", $modal).css({
-            "max-height": $(document).height() * 0.75,
-        });
-        $modal.modal();
+    $(".modal-body", $modal).css({
+      "max-height": $(document).height() * 0.75,
     });
-    $dash1 = $($("#left-top .nav")[0]);
-    $dash1.append($img1);
+    $modal.modal();
+  });
+  $dash1 = $($("#left-top .nav")[0]);
+  $dash1.append($img1);
 };
 one.f.addPopOut();
 
 // bind dashlet nav
 $('.dash .nav a', '#main').click(function() {
-    // de/activation
-    var $li = $(this).parent();
-    var $ul = $li.parent();
-    one.lib.nav.unfocus($ul);
-    $li.addClass('active');
-    // clear respective dashlet
-    var $dashlet = $ul.parent().find('.dashlet');
-    one.lib.dashlet.empty($dashlet);
+  // de/activation
+  var $li = $(this).parent();
+  var $ul = $li.parent();
+  one.lib.nav.unfocus($ul);
+  $li.addClass('active');
+  // clear respective dashlet
+  var $dashlet = $ul.parent().find('.dashlet');
+  one.lib.dashlet.empty($dashlet);
 
-    // callback based on menu
-    var id = $(this).attr('id');
-    var menu = one.f.dashlet;
-    switch (id) {
-        case menu.nodesLearnt.id:
-            one.f.switchmanager.nodesLearnt.dashlet($dashlet);
-            break;
-        case menu.staticRouteConfig.id:
-            one.f.switchmanager.staticRouteConfig.dashlet($dashlet);
-            break;
-        case menu.subnetGatewayConfig.id:
-            one.f.switchmanager.subnetGatewayConfig.dashlet($dashlet);
-            break;
-        case menu.spanPortConfig.id:
-            one.f.switchmanager.spanPortConfig.dashlet($dashlet);
-            break;
-    };
+  // callback based on menu
+  var id = $(this).attr('id');
+  var menu = one.f.dashlet;
+  switch (id) {
+    case menu.nodesLearnt.id:
+      one.f.switchmanager.nodesLearnt.dashlet($dashlet);
+      break;
+    case menu.staticRouteConfig.id:
+      one.f.switchmanager.staticRouteConfig.dashlet($dashlet);
+      break;
+    case menu.subnetGatewayConfig.id:
+      one.f.switchmanager.subnetGatewayConfig.dashlet($dashlet);
+      break;
+    case menu.spanPortConfig.id:
+      one.f.switchmanager.spanPortConfig.dashlet($dashlet);
+      break;
+    case menu.connection.id:
+      one.f.connection.dashlet($dashlet);
+      break;
+  };
 });
 
 // activate first tab on each dashlet
 $('.dash .nav').each(function(index, value) {
-    $($(value).find('li')[0]).find('a').click();
+  $($(value).find('li')[0]).find('a').click();
 });