Merge "Resolve Bug:593. Persister should communicate via OSGi SR instead of TCP."
authorTony Tkacik <ttkacik@cisco.com>
Fri, 28 Mar 2014 19:13:21 +0000 (19:13 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 28 Mar 2014 19:13:21 +0000 (19:13 +0000)
73 files changed:
opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java
opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java
opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java
opendaylight/md-sal/inventory-manager/pom.xml
opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/InventoryMapping.xtend
opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang
opendaylight/md-sal/sal-binding-api/pom.xml
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java
opendaylight/md-sal/sal-common-api/pom.xml
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.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/LogicalDatastoreType.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java [deleted file]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountService.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java
opendaylight/md-sal/statistics-manager/pom.xml
opendaylight/md-sal/test/sal-rest-connector-it/pom.xml
opendaylight/md-sal/topology-manager/pom.xml
opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java
opendaylight/netconf/pom.xml
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java
opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java [new file with mode: 0644]
opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java
opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/Activator.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleBroadcastHandlerImpl.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleForwardingImpl.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java
opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java
opendaylight/web/flows/src/main/resources/js/page.js

index b253179..b7639be 100644 (file)
@@ -24,7 +24,6 @@ import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.opendaylight.controller.sal.packet.IDataPacketService;
 import org.opendaylight.controller.sal.packet.IListenDataPacket;
-import org.opendaylight.controller.sal.routing.IRouting;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.topologymanager.ITopologyManager;
 import org.slf4j.Logger;
@@ -44,6 +43,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * instantiated in order to get an fully working implementation
      * Object
      */
+    @Override
     public Object[] getImplementations() {
         Object[] res = { ArpHandler.class };
         return res;
@@ -62,6 +62,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * also optional per-container different behavior if needed, usually
      * should not be the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
         if (imp.equals(ArpHandler.class)) {
             // export the service
@@ -100,10 +101,6 @@ public class Activator extends ComponentActivatorAbstractBase {
                    "setClusterContainerService", "unsetClusterContainerService")
                    .setRequired(true));
 
-            c.add(createContainerServiceDependency(containerName).setService(
-                   IRouting.class).setCallbacks("setRouting","unsetRouting")
-                   .setRequired(false));
-
             // the Host Listener is optional
             c.add(createContainerServiceDependency(containerName).setService(
                     IfHostListener.class).setCallbacks("setHostListener",
index fa88382..083e2ed 100644 (file)
@@ -113,16 +113,6 @@ public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateA
         }
     }
 
-    void setRouting(IRouting r) {
-        this.routing = r;
-    }
-
-    void unsetRouting(IRouting r) {
-        if (this.routing == r) {
-            this.routing = null;
-        }
-    }
-
     void setHostListener(IfHostListener s) {
         if (this.hostListeners != null) {
             this.hostListeners.add(s);
@@ -507,41 +497,18 @@ public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateA
 
         // see if we know about the host
         // Hosttracker hosts db key implementation
-        IHostId id = HostIdFactory.create(dIP, null);
-        HostNodeConnector host = hostTracker.hostFind(id);
+        HostNodeConnector host = hostTracker.hostFind(dIP);
 
         if (host == null) {
-            // if we don't, know about the host, try to find it
+            // if we don't know about the host, try to find it
             log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
             /*
              * unknown destination host, initiate bcast ARP request
              */
             arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
 
-        } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
-            /*
-             * if IRouting is available, make sure that this packet can get it's
-             * destination normally before teleporting it there. If it's not
-             * available, then assume it's reachable.
-             *
-             * TODO: come up with a way to do this in the absence of IRouting
-             */
-
-            log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
-
-            /*
-             * if we know where the host is and there's a path from where this
-             * packet was punted to where the host is, then deliver it to the
-             * host for now
-             */
-            NodeConnector nc = host.getnodeConnector();
-
-            // re-encode the Ethernet packet (the parent of the IPv4 packet)
-            RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
-            rp.setOutgoingNodeConnector(nc);
-            this.dataPacketService.transmitDataPacket(rp);
         } else {
-            log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
+            log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
         }
     }
 
index e05059b..e7c7bbe 100644 (file)
@@ -91,7 +91,7 @@
     <commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
     <concepts.version>0.5.2-SNAPSHOT</concepts.version>
     <protocol-framework.version>0.5.0-SNAPSHOT</protocol-framework.version>
-    <netty.version>4.0.10.Final</netty.version>
+    <netty.version>4.0.17.Final</netty.version>
     <commons.io.version>2.4</commons.io.version>
     <bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
     <usermanager.version>0.4.2-SNAPSHOT</usermanager.version>
index 8e93583..48fdd88 100644 (file)
@@ -7,9 +7,16 @@
  */
 package org.opendaylight.controller.config.manager.impl.osgi.mapping;
 
+import static java.lang.String.format;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
 import org.apache.commons.io.IOUtils;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry;
 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
@@ -19,14 +26,6 @@ import org.osgi.util.tracker.BundleTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.lang.String.format;
-
 /**
  * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry
  */
@@ -78,7 +77,7 @@ public final class ModuleInfoBundleTracker implements BundleTrackerCustomizer<Co
             return;
         }
 
-        for (Registration<YangModuleInfo> reg : regs) {
+        for (ObjectRegistration<YangModuleInfo> reg : regs) {
             try {
                 reg.close();
             } catch (Exception e) {
index 0d223b8..4598bac 100644 (file)
@@ -49,7 +49,7 @@ netconf.config.persister.2.properties.numberOfBackups=1
 # Set Default start level for framework
 osgi.bundles.defaultStartLevel=4
 # Extra packages to import from the boot class loader
-org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc
+org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
 # This is not Eclipse App
 eclipse.ignoreApp=true
 # Don't shutdown equinox if the eclipse App has ended,
index 3ad08ca..c89cfc1 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.sal.action.Controller;
 import org.opendaylight.controller.sal.action.Drop;
 import org.opendaylight.controller.sal.action.Enqueue;
 import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
 import org.opendaylight.controller.sal.action.HwPath;
 import org.opendaylight.controller.sal.action.Loopback;
 import org.opendaylight.controller.sal.action.Output;
@@ -791,6 +792,27 @@ public class FlowConfig extends ConfigurationObject implements Serializable {
                     continue;
                 }
 
+                sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp);
+                if (sstr.matches()) {
+                    for (String t : sstr.group(1).split(",")) {
+                        if (t != null) {
+                            String parts[] = t.split(":");
+                            String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
+                            if (NodeConnector.fromString(nc) == null) {
+                                return new Status(StatusCode.BADREQUEST, String.format("Enqueue port is not valid"));
+                            }
+                            if (parts.length > 1) {
+                                try {
+                                    Integer.parseInt(parts[1]);
+                                } catch (NumberFormatException e) {
+                                    return new Status(StatusCode.BADREQUEST, String.format("Enqueue %s is not in the range 0 - 2147483647", parts[1]));
+                                }
+                            }
+                        }
+                    }
+                    continue;
+                }
+
                 sstr = Pattern.compile(ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(actiongrp);
                 if (sstr.matches()) {
                     if ((sstr.group(1) != null) && !isVlanPriorityValid(sstr.group(1))) {
@@ -1019,6 +1041,12 @@ public class FlowConfig extends ConfigurationObject implements Serializable {
                     continue;
                 }
 
+                sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp);
+                if (sstr.matches()) {
+                    actionList.add(new FloodAll());
+                    continue;
+                }
+
                 sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp);
                 if (sstr.matches()) {
                     actionList.add(new SwPath());
index 734a392..ce49b59 100644 (file)
@@ -105,7 +105,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
     static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts";
     static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts";
     private static final Logger logger = LoggerFactory.getLogger(HostTracker.class);
-    protected final Set<IHostFinder> hostFinder = new CopyOnWriteArraySet<IHostFinder>();;
+    protected final Set<IHostFinder> hostFinders = new CopyOnWriteArraySet<IHostFinder>();
     protected ConcurrentMap<IHostId, HostNodeConnector> hostsDB;
     /*
      * Following is a list of hosts which have been requested by NB APIs to be
@@ -256,16 +256,12 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
     }
 
     public void setArpHandler(IHostFinder hostFinder) {
-        if (this.hostFinder != null) {
-            this.hostFinder.add(hostFinder);
-        }
+        this.hostFinders.add(hostFinder);
     }
 
     public void unsetArpHandler(IHostFinder hostFinder) {
-        if (this.hostFinder != null) {
-            logger.debug("Arp Handler Service removed!");
-            this.hostFinder.remove(hostFinder);
-        }
+        logger.debug("Arp Handler Service removed!");
+        this.hostFinders.remove(hostFinder);
     }
 
     public void setTopologyManager(ITopologyManager s) {
@@ -352,8 +348,8 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
          * already handles the null return
          */
 
-        if (hostFinder == null) {
-            logger.debug("Exiting hostFind, null hostFinder");
+        if (hostFinders.isEmpty()) {
+            logger.debug("No available host finders, exiting hostFind()");
             return null;
         }
 
@@ -369,7 +365,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
         logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", id);
 
         /* host is not found, initiate a discovery */
-        for (IHostFinder hf : hostFinder) {
+        for (IHostFinder hf : hostFinders) {
             InetAddress addr = decodeIPFromId(id);
             hf.find(addr);
         }
@@ -936,12 +932,12 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
             for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
                 ARPPending arphost;
                 arphost = entry.getValue();
-                if (hostFinder == null) {
-                    logger.warn("ARPHandler Services are not available on subnet addition");
+                if (hostFinders.isEmpty()) {
+                    logger.debug("ARPHandler Services are not available on subnet addition");
                     continue;
                 }
                 logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", decodeIPFromId(arphost.getHostId()));
-                for (IHostFinder hf : hostFinder) {
+                for (IHostFinder hf : hostFinders) {
                     hf.find(decodeIPFromId(arphost.getHostId()));
                 }
             }
@@ -977,11 +973,11 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                          * next one. Before sending the ARP, check if ARPHandler
                          * is available or not
                          */
-                        if (hostFinder == null) {
+                        if (hostFinders.isEmpty()) {
                             logger.warn("ARPHandler Services are not available for Outstanding ARPs");
                             continue;
                         }
-                        for (IHostFinder hf : hostFinder) {
+                        for (IHostFinder hf : hostFinders) {
                             hf.find(decodeIPFromId(arphost.getHostId()));
                         }
                         arphost.sent_count++;
@@ -1063,7 +1059,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                                             HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
                         }
                         host.setArpSendCountDown(arp_cntdown);
-                        if (hostFinder == null) {
+                        if (hostFinders.isEmpty()) {
                             /*
                              * If hostfinder is not available, then can't send
                              * the probe. However, continue the age out the
@@ -1073,7 +1069,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                             logger.trace("ARPHandler is not avaialable, can't send the probe");
                             continue;
                         }
-                        for (IHostFinder hf : hostFinder) {
+                        for (IHostFinder hf : hostFinders) {
                             hf.probe(host);
                         }
                     }
@@ -1417,7 +1413,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
         for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
             arphost = entry.getValue();
             logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostId());
-            if (hostFinder == null) {
+            if (hostFinders.isEmpty()) {
                 logger.warn("ARPHandler is not available at interface  up");
                 logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
                         nodeConnector);
@@ -1430,7 +1426,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                 byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
                 host = new HostNodeConnector(dataLayerAddress, decodeIPFromId(arphost.getHostId()), nodeConnector,
                         (short) 0);
-                for (IHostFinder hf : hostFinder) {
+                for (IHostFinder hf : hostFinders) {
                     hf.probe(host);
                 }
             } catch (ConstructionException e) {
index 4d0465c..362a349 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
index 7668a31..6f1ef5e 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.md.inventory.manager
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowNodeConnector
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode
index c4cccc1..19d6eaf 100644 (file)
@@ -2,7 +2,7 @@ module opendaylight-statistics-types {
     namespace "urn:opendaylight:model:statistics:types";
     prefix stat-types;
 
-    import ietf-yang-types {prefix yang;}
+    import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
     
     revision "2013-09-25" {
         description "Initial revision of flow service";
index f90031b..00129b9 100644 (file)
   </scm>
 
     <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-common</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common</artifactId>
+            <artifactId>sal-common-api</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common-api</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java
new file mode 100644 (file)
index 0000000..c6a9efe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+    @Override
+    BindingDataReadTransaction newReadOnlyTransaction();
+
+    @Override
+    BindingDataReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    BindingDataWriteTransaction newWriteOnlyTransaction();
+
+    @Override
+    ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+            InstanceIdentifier<?> path, BindingDataChangeListener listener, DataChangeScope triggeringScope);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java
new file mode 100644 (file)
index 0000000..94ac2d2
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier<?>, DataObject> {
+    @Override
+    void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java
new file mode 100644 (file)
index 0000000..93df3eb
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface BindingDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+    @Override
+    ListenableFuture<Optional<DataObject>> read(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..0dcf020
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Logical capture of a combination of both {@link BindingDataReadTransaction} and
+ * {@link BindingDataWriteTransaction}.
+ */
+public interface BindingDataReadWriteTransaction extends BindingDataReadTransaction, BindingDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..e989f73
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+    @Override
+    void put(LogicalDatastoreType store, InstanceIdentifier<?> path, DataObject data);
+
+    @Override
+    void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
index db0d674..5b70070 100644 (file)
@@ -11,7 +11,7 @@ import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.RpcService;
@@ -19,20 +19,20 @@ import org.osgi.framework.BundleContext;
 
 /**
  * Binding-aware core of the SAL layer responsible for wiring the SAL consumers.
- * 
+ *
  * The responsibility of the broker is to maintain registration of SAL
  * functionality {@link Consumer}s and {@link Provider}s, store provider and
  * consumer specific context and functionality registration via
  * {@link ConsumerContext} and provide access to infrastructure services, which
  * removes direct dependencies between providers and consumers.
- * 
+ *
  * The Binding-aware broker is also responsible for translation from Java
  * classes modeling the functionality and data to binding-independent form which
  * is used in SAL Core.
- * 
- * 
+ *
+ *
  * <h3>Infrastructure services</h3> Some examples of infrastructure services:
- * 
+ *
  * <ul>
  * <li>YANG Module service - see {@link ConsumerContext#getRpcService(Class)},
  * {@link ProviderContext}
@@ -42,34 +42,34 @@ import org.osgi.framework.BundleContext;
  * <li>Data Store access and modification - see {@link DataBrokerService} and
  * {@link DataProviderService}
  * </ul>
- * 
+ *
  * The services are exposed via session.
- * 
+ *
  * <h3>Session-based access</h3>
- * 
+ *
  * The providers and consumers needs to register in order to use the
  * binding-independent SAL layer and to expose functionality via SAL layer.
- * 
+ *
  * For more information about session-based access see {@link ConsumerContext}
  * and {@link ProviderContext}
- * 
- * 
- * 
+ *
+ *
+ *
  */
 public interface BindingAwareBroker {
     /**
      * Registers the {@link BindingAwareConsumer}, which will use the SAL layer.
-     * 
+     *
      * <p>
      * Note that consumer could register additional functionality at later point
      * by using service and functionality specific APIs.
-     * 
+     *
      * <p>
      * The consumer is required to use returned session for all communication
      * with broker or one of the broker services. The session is announced to
      * the consumer by invoking
      * {@link Consumer#onSessionInitiated(ConsumerContext)}.
-     * 
+     *
      * @param cons
      *            Consumer to be registered.
      * @return a session specific to consumer registration
@@ -82,24 +82,24 @@ public interface BindingAwareBroker {
 
     /**
      * Registers the {@link BindingAwareProvider}, which will use the SAL layer.
-     * 
+     *
      * <p>
      * During the registration, the broker obtains the initial functionality
      * from consumer, using the
      * {@link BindingAwareProvider#getImplementations()}, and register that
      * functionality into system and concrete infrastructure services.
-     * 
+     *
      * <p>
      * Note that provider could register additional functionality at later point
      * by using service and functionality specific APIs.
-     * 
+     *
      * <p>
      * The consumer is <b>required to use</b> returned session for all
      * communication with broker or one of the broker services. The session is
      * announced to the consumer by invoking
      * {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}.
-     * 
-     * 
+     *
+     *
      * @param prov
      *            Provider to be registered.
      * @return a session unique to the provider registration.
@@ -112,26 +112,26 @@ public interface BindingAwareBroker {
 
     /**
      * {@link BindingAwareConsumer} specific access to the SAL functionality.
-     * 
+     *
      * <p>
      * ConsumerSession is {@link BindingAwareConsumer}-specific access to the
      * SAL functionality and infrastructure services.
-     * 
+     *
      * <p>
      * The session serves to store SAL context (e.g. registration of
      * functionality) for the consumer and provides access to the SAL
      * infrastructure services and other functionality provided by
      * {@link Provider}s.
-     * 
-     * 
-     * 
+     *
+     *
+     *
      */
     public interface ConsumerContext extends RpcConsumerRegistry {
 
         /**
          * Returns a session specific instance (implementation) of requested
          * binding-aware infrastructural service
-         * 
+         *
          * @param service
          *            Broker service
          * @return Session specific implementation of service
@@ -143,19 +143,19 @@ public interface BindingAwareBroker {
 
     /**
      * {@link BindingAwareProvider} specific access to the SAL functionality.
-     * 
+     *
      * <p>
      * ProviderSession is {@link BindingAwareProvider}-specific access to the
      * SAL functionality and infrastructure services, which also allows for
      * exposing the provider's functionality to the other
      * {@link BindingAwareConsumer}s.
-     * 
+     *
      * <p>
      * The session serves to store SAL context (e.g. registration of
      * functionality) for the providers and exposes access to the SAL
      * infrastructure services, dynamic functionality registration and any other
      * functionality provided by other {@link BindingAwareConsumer}s.
-     * 
+     *
      */
     public interface ProviderContext extends ConsumerContext, RpcProviderRegistry {
 
@@ -166,7 +166,7 @@ public interface BindingAwareBroker {
         void unregisterFunctionality(ProviderFunctionality functionality);
     }
 
-    public interface RpcRegistration<T extends RpcService> extends Registration<T> {
+    public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
 
         Class<T> getServiceType();
     }
@@ -177,9 +177,9 @@ public interface BindingAwareBroker {
         /**
          * Register particular instance identifier to be processed by this
          * RpcService
-         * 
-         * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}. 
-         * 
+         *
+         * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+         *
          * @param context
          * @param instance
          */
@@ -189,9 +189,9 @@ public interface BindingAwareBroker {
         /**
          * Unregister particular instance identifier to be processed by this
          * RpcService
-         * 
-         * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}. 
-         * 
+         *
+         * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+         *
          * @param context
          * @param instance
          */
index e0c7d26..542dfa7 100644 (file)
@@ -247,7 +247,8 @@ public class RpcProviderRegistryImpl implements //
 
         public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
             super(service);
-            serviceType = type;
+            this.serviceType = type;
+            this.registry =  registry;
         }
 
         @Override
index 0a71ef5..ba639ad 100644 (file)
@@ -45,6 +45,13 @@ public class TestHelper {
                 bindingAwareSalBundles(),
                 mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
 
+                systemProperty("org.osgi.framework.system.packages.extra").value("sun.nio.ch"),
+                mavenBundle("io.netty", "netty-common").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
+
                 mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
                 mavenBundle("commons-io", "commons-io").versionAsInProject(), //
@@ -64,12 +71,6 @@ public class TestHelper {
 
                 mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
 
-                mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-common").versionAsInProject(), //
-
                 mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"),
                 mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
 
index 9ac94e7..019fc0e 100644 (file)
@@ -25,6 +25,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn
 import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
 import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 @RunWith(PaxExam.class)
@@ -70,6 +71,7 @@ public abstract class AbstractTest {
                 mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
                 mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+                systemPackages("sun.nio.ch"),
 
                 mdSalCoreBundles(),
 
index 8a2571c..8798897 100644 (file)
@@ -1,23 +1,23 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>sal-parent</artifactId>
         <version>1.1-SNAPSHOT</version>
     </parent>
+
     <artifactId>sal-common-api</artifactId>
+    <packaging>bundle</packaging>
+
     <scm>
-      <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
-      <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
-      <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
-      <tag>HEAD</tag>
-  </scm>
+        <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+        <tag>HEAD</tag>
+    </scm>
 
     <dependencies>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-common</artifactId>
@@ -27,9 +27,8 @@
             <artifactId>concepts</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
     </dependencies>
-    <packaging>bundle</packaging>
 </project>
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java
new file mode 100644 (file)
index 0000000..87bbfd3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+        AsyncDataTransactionFactory<P, D> {
+
+    /**
+     *
+     * Scope of Data Change
+     *
+     * Represents scope of data change (addition, replacement, deletion).
+     *
+     * The terminology for types is reused from LDAP
+     *
+     * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml
+     */
+    public enum DataChangeScope {
+
+       /**
+        * Represents only a direct change of the node, such as replacement of node,
+        * addition or deletion.
+        *
+        */
+       BASE,
+       /**
+        * Represent a change (addition,replacement,deletion)
+        * of the node or one of it's direct childs.
+        *
+        */
+       ONE,
+       /**
+        * Represents a change of the node or any of it's child nodes.
+        *
+        */
+       SUBTREE
+    }
+
+    @Override
+    public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+    @Override
+    public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+
+    @Override
+    public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+    /**
+     * Registers {@link DataChangeListener} for Data Change callbacks
+     * which will be triggered on which will be triggered on the store
+     *
+     * @param store Logical store in which listener is registered.
+     * @param path Path (subtree identifier) on which client listener will be invoked.
+     * @param listener Instance of listener which should be invoked on
+     * @param triggeringScope Scope of change which triggers callback.
+     * @return Listener registration of the listener, call {@link ListenerRegistration#close()}
+     *         to stop delivery of change events.
+     */
+    ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java
new file mode 100644 (file)
index 0000000..f612e51
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+    /**
+     * Returns a immutable map of paths and newly created objects
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedData();
+
+    /**
+     * Returns a immutable map of paths and respective updated objects after update.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedData();
+
+    /**
+     * Returns a immutable set of removed paths.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalData()}
+     *
+     * @return set of removed paths
+     */
+    Set<P> getRemovedPaths();
+
+    /**
+     * Return a immutable map of paths and original state of updated and removed objects.
+     *
+     * This map is populated if at changed path was previous object, and captures
+     * state of previous object.
+     *
+     * @return map of paths and original state of updated and removed objects.
+     */
+    Map<P, ? extends D> getOriginalData();
+
+    /**
+     * Returns a  immutable stable view of data state, which
+     * captures state of data store before the reported change.
+     *
+     *
+     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+     *
+     * @return Stable view of data before the change happened, rooted at the listener registration path.
+     *
+     */
+    D getOriginalSubtree();
+
+    /**
+     * Returns a immutable stable view of data, which captures state of data store
+     * after the reported change.
+     *
+     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+     *
+     * @return Stable view of data after the change happened, rooted at the listener registration path.
+     */
+    D getUpdatedSubtree();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java
new file mode 100644 (file)
index 0000000..49f07bc
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
+    /**
+     * Note that this method may be invoked from a shared thread pool, so
+     * implementations SHOULD NOT perform CPU-intensive operations and they
+     * definitely MUST NOT invoke any potentially blocking operations.
+     *
+     * @param change Data Change Event being delivered.
+     */
+    void onDataChanged(AsyncDataChangeEvent<P, D> change);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java
new file mode 100644 (file)
index 0000000..732fed0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+    AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+    AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+    AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java
new file mode 100644 (file)
index 0000000..1d1d910
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+    /**
+     *
+     * Reads data from provided logical data store located at provided path
+     *
+     *
+     * @param store
+     *            Logical data store from which read should occur.
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return Listenable Future which contains read result
+     *         <ul>
+     *         <li>If data at supplied path exists the {@link Future#get()}
+     *         returns Optional object containing data
+     *         <li>If data at supplied path does not exists the
+     *         {@link Future#get()} returns {@link Optional#absent()}.
+     *         </ul>
+     */
+    ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..ce740bf
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling client to have combined transaction,
+ * which provides read and write capabilities.
+ *
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ */
+public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
+        AsyncWriteTransaction<P, D> {
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java
new file mode 100644 (file)
index 0000000..23ca275
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * @author
+ *
+ * @param <P> Type of path (subtree identifier), which represents location in tree
+ * @param <D> Type of data (payload), which represents data payload
+ */
+public interface AsyncTransaction<P extends Path<P>,D> extends //
+    Identifiable<Object>,
+    AutoCloseable {
+
+    @Override
+    public Object getIdentifier();
+
+    /**
+     * Closes transaction and releases all resources associated with it.
+     */
+    @Override
+    public void close();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java
new file mode 100644 (file)
index 0000000..35b9914
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface AsyncWriteTransaction<P extends Path<P>, D>  extends AsyncTransaction<P, D> {
+    /**
+     * Cancels transaction.
+     *
+     * Transaction could be only cancelled if it's status
+     * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+     *
+     * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED}
+     * will have no effect.
+     *
+     * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+     *
+     */
+    public void cancel();
+
+    /**
+     * Store a piece of data at specified path. This acts as a add / replace operation,
+     * which is to say that whole subtree will be replaced by specified path.
+     *
+     * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)}
+     *
+     * @param store Logical data store which should be modified
+     * @param path Data object path
+     * @param data Data object to be written to specified path
+     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    public void put(LogicalDatastoreType store, P path, D data);
+
+    /**
+     * Store a piece of data at specified path. This acts as a merge operation,
+     * which is to say that any pre-existing data which is not explicitly
+     * overwritten will be preserved. This means that if you store a container,
+     * its child lists will be merged. Performing the following put operations:
+     *
+     * 1) container { list [ a ] }
+     * 2) container { list [ b ] }
+     *
+     * will result in the following data being present:
+     *
+     * container { list [ a, b ] }
+     *
+     * This also means that storing the container will preserve any augmentations
+     * which have been attached to it.
+     *
+     * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+     *
+     * @param store Logical data store which should be modified
+     * @param path Data object path
+     * @param data Data object to be written to specified path
+     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    public void merge(LogicalDatastoreType store, P path, D data);
+
+    /**
+     * Remove a piece of data from specified path. This operation does not fail
+     * if the specified path does not exist.
+     *
+     * @param store Logical data store which should be modified
+     * @param path Data object path
+     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    public void delete(LogicalDatastoreType store, P path);
+
+    /**
+     *
+     * Closes transaction and resources allocated to the transaction.
+     *
+     * This call does not change Transaction status. Client SHOULD
+     * explicitly {@link #commit()} or {@link #cancel()} transaction.
+     *
+     * @throws IllegalStateException if the transaction has not been
+     *         updated by invoking {@link #commit()} or {@link #cancel()}.
+     */
+    @Override
+    public void close();
+
+    /**
+     * Initiates a commit of modification. This call logically seals the
+     * transaction, preventing any the client from interacting with the
+     * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
+     * and enqueued into the data store backed for processing.
+     *
+     * <p>
+     * The successful commit changes the state of the system and may affect
+     * several components.
+     *
+     * <p>
+     * The effects of successful commit of data are described in the
+     * specifications and YANG models describing the Provider components of
+     * controller. It is assumed that Consumer has an understanding of this
+     * changes.
+     *
+     * @see DataCommitHandler for further information how two-phase commit is
+     *      processed.
+     * @param store Identifier of the store, where commit should occur.
+     * @return Result of the Commit, containing success information or list of
+     *         encountered errors, if commit was not successful. The Future
+     *         blocks until {@link TransactionStatus#COMMITED} or
+     *         {@link TransactionStatus#FAILED} is reached.
+     * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+     */
+    public Future<RpcResult<TransactionStatus>> commit();
+
+}
index 8787a3f..669baa8 100644 (file)
@@ -12,6 +12,12 @@ import java.util.EventListener;
 import org.opendaylight.yangtools.concepts.Path;
 
 public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
-
+    /**
+     * Note that this method may be invoked from a shared thread pool, so
+     * implementations SHOULD NOT perform CPU-intensive operations and they
+     * definitely MUST NOT invoke any potentially blocking operations.
+     *
+     * @param change Data Change Event being delivered.
+     **/
     void onDataChanged(DataChangeEvent<P, D> change);
 }
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java
new file mode 100644 (file)
index 0000000..d2e41f1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+public enum LogicalDatastoreType {
+
+    /**
+     * Logical atastore representing operational state of the system
+     * and it's components
+     *
+     * This datastore is used to describe operational state of
+     * the system and it's operation related data.
+     *
+     */
+    OPERATIONAL,
+    /**
+     * Logical Datastore representing configuration state of the system
+     * and it's components.
+     *
+     * This datastore is used to describe intended state of
+     * the system and intended operation mode.
+     *
+     */
+    CONFIGURATION
+
+}
index d542935..e7e0eb0 100644 (file)
@@ -13,17 +13,34 @@ import org.opendaylight.yangtools.concepts.Path;
  * A chain of transactions. Transactions in a chain need to be committed in sequence and each
  * transaction should see the effects of previous transactions as if they happened. A chain
  * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
  */
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable {
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable, AsyncDataTransactionFactory<P, D> {
+
     /**
-     * Create a new transaction which will continue the chain. The previous transaction
-     * has to be either COMMITTED or CANCELLED.
+     * Create a new read only transaction which will continue the chain.
+     * The previous read-write transaction has to be either COMMITED or CANCELLED.
      *
      * @return New transaction in the chain.
-     * @throws IllegalStateException if the previous transaction was not COMMITTED or CANCELLED.
+     * @throws IllegalStateException if the previous transaction was not COMMITED
+     *    or CANCELLED.
      * @throws TransactionChainClosedException if the chain has been closed.
      */
-    DataModification<P, D> newTransaction();
+    @Override
+    public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+
+    /**
+     * Create a new read write transaction which will continue the chain.
+     * The previous read-write transaction has to be either COMMITED or CANCELLED.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not COMMITTED
+     *    or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
 
     @Override
     void close();
index 4dac6f5..52b0812 100644 (file)
@@ -21,7 +21,7 @@ public interface TransactionChainListener extends EventListener {
      * @param transaction Transaction which caused the chain to fail
      * @param cause The cause of transaction failure
      */
-    void onTransactionChainFailed(TransactionChain<?, ?> chain, DataModification<?, ?> transaction, Throwable cause);
+    void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
 
     /**
      * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java
deleted file mode 100644 (file)
index bb8594f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.md.sal.common.impl;
-
-import org.opendaylight.yangtools.concepts.Registration;
-
-public abstract class AbstractRegistration<T> implements Registration<T> {
-
-
-    private final T instance;
-
-    public AbstractRegistration(T instance) {
-        super();
-        this.instance = instance;
-    }
-
-    @Override
-    public final T getInstance() {
-        return instance;
-    }
-
-}
index 4ffb87d..22c458a 100644 (file)
@@ -8,9 +8,10 @@
 package org.opendaylight.controller.md.sal.common.impl;
 
 import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.Path;
 
-public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractRegistration<S> implements
+public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractObjectRegistration<S> implements
         RoutedRegistration<C, P, S> {
 
     public AbstractRoutedRegistration(S instance) {
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java
new file mode 100644 (file)
index 0000000..5328b79
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataBroker extends AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>{
+    @Override
+    DOMDataReadTransaction newReadOnlyTransaction();
+
+    @Override
+    DOMDataReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    DOMDataWriteTransaction newWriteOnlyTransaction();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java
new file mode 100644 (file)
index 0000000..d1f0176
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java
new file mode 100644 (file)
index 0000000..5baa5e7
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..55600b0
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..9415973
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
index 6af0625..72df8cb 100644 (file)
@@ -16,7 +16,7 @@ import org.opendaylight.controller.sal.core.api.data.DataProviderService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationService;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -237,7 +237,7 @@ public interface Broker {
         ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
     }
 
-    public interface RpcRegistration extends Registration<RpcImplementation> {
+    public interface RpcRegistration extends ObjectRegistration<RpcImplementation> {
         QName getType();
 
         @Override
index 18c8546..5698b96 100644 (file)
@@ -8,8 +8,6 @@
 
 package org.opendaylight.controller.sal.core.api.mount;
 
-import java.util.concurrent.Future;
-
 import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationService;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -17,11 +15,29 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface representing a single mount instance and represents a way for
+ * clients to access underlying data, RPCs and notifications.
+ */
 public interface MountInstance extends //
         NotificationService, //
         DataBrokerService {
 
-    Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+    /**
+     * Invoke an RPC on the system underlying the mount instance.
+     *
+     * @param type RPC type
+     * @param input RPC input arguments
+     * @return Future representing execution of the RPC.
+     */
+    ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
 
+    /**
+     * Get {@link SchemaContext} of the system underlying the mount instance.
+     *
+     * @return A schema context.
+     */
     SchemaContext getSchemaContext();
 }
index c4c7889..1185c45 100644 (file)
@@ -16,18 +16,18 @@ public interface MountProvisionService extends MountService {
 
     @Override
     public MountProvisionInstance getMountPoint(InstanceIdentifier path);
-    
+
     MountProvisionInstance createMountPoint(InstanceIdentifier path);
-    
+
     MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
-    
+
     ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
-    
-    public  interface MountProvisionListener extends EventListener {
-        
+
+    public interface MountProvisionListener extends EventListener {
+
         void onMountPointCreated(InstanceIdentifier path);
-        
+
         void onMountPointRemoved(InstanceIdentifier path);
-        
+
     }
 }
index 3180271..6d1f172 100644 (file)
@@ -10,8 +10,16 @@ package org.opendaylight.controller.sal.core.api.mount;
 import org.opendaylight.controller.sal.core.api.BrokerService;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
-
+/**
+ * Client-level interface for interacting with mount points. It provides access
+ * to {@link MountInstance} instances based on their path.
+ */
 public interface MountService extends BrokerService {
-
+    /**
+     * Obtain access to a mount instance registered at the specified path.
+     *
+     * @param path Path at which the instance is registered
+     * @return Reference to the instance, or null if no such instance exists.
+     */
     MountInstance getMountPoint(InstanceIdentifier path);
 }
index dc554a0..f9f977e 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.controller.sal.dom.broker;
 
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.Future;
 
 import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
@@ -41,6 +40,8 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 public class MountPointImpl implements MountProvisionInstance, SchemaContextProvider {
 
     private final SchemaAwareRpcBroker rpcs;
@@ -135,7 +136,7 @@ public class MountPointImpl implements MountProvisionInstance, SchemaContextProv
 
 
     @Override
-    public Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+    public ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
         return null;
     }
 
index 50af3fb..7fba311 100644 (file)
@@ -11,7 +11,7 @@ import java.util.Collection;
 
 import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
 import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -25,12 +25,12 @@ import com.google.common.collect.Multimaps;
 public class NotificationRouterImpl implements NotificationRouter {
     private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class);
 
-    private final Multimap<QName, Registration<NotificationListener>> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, Registration<NotificationListener>>create());
+    private final Multimap<QName, ListenerRegistration> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, ListenerRegistration>create());
 //    private Registration<NotificationListener> defaultListener;
 
     private void sendNotification(CompositeNode notification) {
         final QName type = notification.getNodeType();
-        final Collection<Registration<NotificationListener>> toNotify = listeners.get(type);
+        final Collection<ListenerRegistration> toNotify = listeners.get(type);
         log.trace("Publishing notification " + type);
 
         if ((toNotify == null) || toNotify.isEmpty()) {
@@ -38,7 +38,7 @@ public class NotificationRouterImpl implements NotificationRouter {
             return;
         }
 
-        for (Registration<NotificationListener> listener : toNotify) {
+        for (ListenerRegistration listener : toNotify) {
             try {
                 // FIXME: ensure that notification is immutable
                 listener.getInstance().onNotification(notification);
@@ -60,7 +60,7 @@ public class NotificationRouterImpl implements NotificationRouter {
         return ret;
     }
 
-    private class ListenerRegistration extends AbstractObjectRegistration<NotificationListener> {
+    private class ListenerRegistration extends AbstractListenerRegistration<NotificationListener> {
 
         final QName type;
 
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java
new file mode 100644 (file)
index 0000000..c82a2b8
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStore {
+
+    /**
+     *
+     * Creates a read only transaction
+     *
+     * @return
+     */
+    DOMStoreReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Creates write only transaction
+     *
+     * @return
+     */
+    DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * Creates Read-Write transaction
+     *
+     * @return
+     */
+    DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * Registers {@link DataChangeListener} for Data Change callbacks
+     * which will be triggered on the change of provided subpath. What
+     * constitutes a change depends on the @scope parameter.
+     *
+     * Listener upon registration receives an initial callback
+     * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)}
+     * which contains stable view of data tree at the time of registration.
+     *
+     * @param path Path (subtree identifier) on which client listener will be invoked.
+     * @param listener Instance of listener which should be invoked on
+     * @param scope Scope of change which triggers callback.
+     * @return Listener Registration object, which client may use to close registration
+     *         / interest on receiving data changes.
+     *
+     */
+    <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+            InstanceIdentifier path, L listener, DataChangeScope scope);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java
new file mode 100644 (file)
index 0000000..733c109
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface DOMStoreReadTransaction extends DOMStoreTransaction {
+
+    /**
+     *
+     * Reads data from provided logical data store located at provided path
+     *
+     *
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return Listenable Future which contains read result
+     *         <ul>
+     *         <li>If data at supplied path exists the {@link Future#get()}
+     *         returns Optional object containing data
+     *         <li>If data at supplied path does not exists the
+     *         {@link Future#get()} returns {@link Optional#absent()}.
+     *         </ul>
+     */
+    ListenableFuture<Optional<NormalizedNode<?,?>>> read(InstanceIdentifier path);
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..7277406
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}.
+ */
+public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java
new file mode 100644 (file)
index 0000000..986a153
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction}
+ * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the
+ * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol,
+ * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}.
+ */
+public interface DOMStoreThreePhaseCommitCohort {
+
+    /**
+     * Sends transaction associated with this three phase commit instance to the
+     * participant, participant votes on the transaction, if the transaction
+     * should be committed or aborted.
+     *
+     * @return ListenableFuture with vote of the participant. Vote
+     *         {@link ListenableFuture#get()} is following:
+     *         <ul>
+     *         <li>
+     *         true if transaction is approved by data store.
+     *         <li>false if the transaction is not approved by data store and
+     *         should be aborted.
+     */
+    ListenableFuture<Boolean> canCommit();
+
+    /**
+     * Initiates a pre-commit phase of associated transaction on datastore.
+     *
+     * This message is valid only and only if and only if the participant responded
+     * on {@link #canCommit()} call with positive response.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that pre-commit message was received and processed.
+     */
+    ListenableFuture<Void> preCommit();
+
+    /**
+     * Initiates a abort phase of associated transaction on data store.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that abort message was received.
+     */
+    ListenableFuture<Void> abort();
+
+    /**
+     * Initiates a commit phase on of associated transaction on data store.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that commit message was received and commit of transaction was
+     *        processed.
+     */
+    ListenableFuture<Void> commit();
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java
new file mode 100644 (file)
index 0000000..76ea78b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+/**
+ * DOM Data Store transaction
+ *
+ * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction}
+ * for specific transaction types.
+ *
+ */
+public interface DOMStoreTransaction extends AutoCloseable, Identifiable<Object> {
+    /**
+     * Unique identifier of the transaction
+     */
+    @Override
+    public Object getIdentifier();
+
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java
new file mode 100644 (file)
index 0000000..6761bc1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStoreWriteTransaction extends DOMStoreTransaction {
+
+    /**
+     * Store a provided data at specified path. This acts as a add / replace
+     * operation, which is to say that whole subtree will be replaced by
+     * specified path.
+     *
+     * If you need add or merge of current object with specified use
+     * {@link #merge(LogicalDatastoreType, Path, Object)}
+     *
+     *
+     * @param path
+     * @param data
+     *            Data object to be written
+     *
+     * @throws IllegalStateException
+     *             if the client code already sealed transaction and invoked
+     *             {@link #ready()}
+     */
+    void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     *
+     * Deletes data and whole subtree located at provided path.
+     *
+     * @param path
+     *            Path to delete
+     * @throws IllegalStateException
+     *             if the client code already sealed transaction and invoked
+     *             {@link #ready()}
+     */
+    void delete(InstanceIdentifier path);
+
+    /**
+     *
+     * Seals transaction, and returns three-phase commit cohort associated
+     * with this transaction and DOM Store to be coordinated by coordinator.
+     *
+     * @return Three Phase Commit Cohort instance for this transaction.
+     */
+    DOMStoreThreePhaseCommitCohort ready();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java
new file mode 100644 (file)
index 0000000..ec3b698
--- /dev/null
@@ -0,0 +1,8 @@
+/*\r
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi.data;
\ No newline at end of file
index 38a4dd4..000783b 100644 (file)
@@ -52,6 +52,7 @@ public class ToasterTest {
                 mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
                 mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+                systemPackages("sun.nio.ch"),
 
                 toasterBundles(),
                 mdSalCoreBundles(),
index a9c1e05..b829990 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
         
         <dependency>
             <groupId>junit</groupId>
index 15fee78..a9fc739 100644 (file)
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
-            <version>4.0.10.Final</version>
+           <version>${netty.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
index 525ab9e..8035f42 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
index 036fe73..73e03d1 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.yangtools.yang.binding.NotificationListener
 import org.slf4j.LoggerFactory
 import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
+import org.osgi.framework.BundleContext;
 
 class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implements AutoCloseable {
 
@@ -35,7 +36,11 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement
        LOG.info("FlowCapableTopologyProvider stopped.");
         listenerRegistration?.close();
     }
-    
+
+     /**
+       * Gets called on start of a bundle.
+       * @param session
+       */
     override onSessionInitiated(ProviderContext session) {
         dataService = session.getSALService(DataProviderService)
         notificationService = session.getSALService(NotificationProviderService)
@@ -43,6 +48,14 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement
         exporter.start();
         listenerRegistration = notificationService.registerNotificationListener(exporter);
     }
+
+    /**
+      * Gets called during stop bundle
+      * @param context The execution context of the bundle being stopped.
+      */
+    override stopImpl(BundleContext context) {
+        close();
+    }
     
 }
 
index 5fd9f2f..ee971a6 100644 (file)
@@ -44,6 +44,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn
 import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
 import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 @RunWith(PaxExam.class)
@@ -62,6 +63,7 @@ public class IdentityRefNetconfTest {
                 systemProperty("osgi.console").value("2401"),
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
                 systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+                systemPackages("sun.nio.ch"),
 
                 testingModules(),
                 loggingModules(),
index 204bf1d..e07c7ed 100644 (file)
@@ -109,8 +109,8 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
                                 netconf_ssh_output.setDaemon(false);
                                 netconf_ssh_output.start();
 
-                            } catch (Throwable t){
-                                logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
+                            } catch (Exception t) {
+                                logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
 
                                 try {
                                     if (netconf_ssh_input!=null){
@@ -129,7 +129,6 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
                                     Thread.currentThread().interrupt();
                                     logger.error("netconf_ssh_output join error ",e);
                                 }
-
                             }
                         } else {
                             try {
index e7b9a02..586366f 100644 (file)
@@ -47,7 +47,6 @@
         <osgi.version>5.0.0</osgi.version>
         <maven.bundle.version>2.4.0</maven.bundle.version>
         <slf4j.version>1.7.2</slf4j.version>
-        <netconf.netty.version>4.0.10.Final</netconf.netty.version>
         <salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
     </properties>
 
index 805f5be..9ddba67 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.controller.northbound.commons.exception.NotAcceptableExc
 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
 import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
 import org.opendaylight.controller.sal.utils.ServiceHelper;
@@ -114,7 +115,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.CREATED).build();
            }
-       } catch (Throwable t) {
+       } catch (BridgeDomainConfigServiceException e) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
@@ -161,7 +162,7 @@ public class BridgeDomainNorthbound {
           if (status.getCode().equals(StatusCode.SUCCESS)) {
               return Response.status(Response.Status.OK).build();
           }
-      } catch (Throwable t) {
+      } catch (Exception t) {
           return Response.status(Response.Status.PRECONDITION_FAILED).build();
       }
       throw new ResourceNotFoundException(status.getDescription());
@@ -215,7 +216,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.CREATED).build();
            }
-       } catch (Throwable t) {
+       } catch (Exception t) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
@@ -264,7 +265,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.OK).build();
            }
-       } catch (Throwable t) {
+       } catch (Exception t) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
index aa60f91..f27d30e 100644 (file)
@@ -54,6 +54,12 @@ public class SecureMessageReadWriteService implements IMessageReadWrite {
                                     // switch
     private ByteBuffer peerNetData; // encrypted message from the switch
     private FileInputStream kfd = null, tfd = null;
+    private final String keyStoreFileDefault = "./configuration/tlsKeyStore";
+    private final String trustStoreFileDefault = "./configuration/tlsTrustStore";
+    private final String keyStorePasswordPropName = "controllerKeyStorePassword";
+    private final String trustStorePasswordPropName = "controllerTrustStorePassword";
+    private static String keyStorePassword = null;
+    private static String trustStorePassword = null;
 
     public SecureMessageReadWriteService(SocketChannel socket, Selector selector)
             throws Exception {
@@ -80,32 +86,44 @@ public class SecureMessageReadWriteService implements IMessageReadWrite {
      */
     private void createSecureChannel(SocketChannel socket) throws Exception {
         String keyStoreFile = System.getProperty("controllerKeyStore");
-        String keyStorePassword = System
-                .getProperty("controllerKeyStorePassword");
         String trustStoreFile = System.getProperty("controllerTrustStore");
-        String trustStorePassword = System
-                .getProperty("controllerTrustStorePassword");
+        String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName);
+        String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName);
 
         if (keyStoreFile != null) {
             keyStoreFile = keyStoreFile.trim();
+        } else {
+            keyStoreFile = keyStoreFileDefault;
         }
         if ((keyStoreFile == null) || keyStoreFile.isEmpty()) {
             throw new FileNotFoundException("TLS KeyStore file not found.");
         }
+
+        if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) {
+            keyStorePassword = keyStorePasswordProp;
+        }
         if (keyStorePassword != null) {
             keyStorePassword = keyStorePassword.trim();
+            System.setProperty(keyStorePasswordPropName, "");
         }
         if ((keyStorePassword == null) || keyStorePassword.isEmpty()) {
             throw new FileNotFoundException("TLS KeyStore Password not provided.");
         }
         if (trustStoreFile != null) {
             trustStoreFile = trustStoreFile.trim();
+        } else {
+            trustStoreFile = trustStoreFileDefault;
         }
         if ((trustStoreFile == null) || trustStoreFile.isEmpty()) {
             throw new FileNotFoundException("TLS TrustStore file not found");
         }
+
+        if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) {
+            trustStorePassword = trustStorePasswordProp;
+        }
         if (trustStorePassword != null) {
             trustStorePassword = trustStorePassword.trim();
+            System.setProperty(trustStorePasswordPropName, "");
         }
         if ((trustStorePassword == null) || trustStorePassword.isEmpty()) {
             throw new FileNotFoundException("TLS TrustStore Password not provided.");
diff --git a/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java
new file mode 100644 (file)
index 0000000..19f467e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.networkconfig.bridgedomain;
+
+/**
+ * Exception thrown by IPluginInBridgeDomainConfigService implementations.
+ */
+public class BridgeDomainConfigServiceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public BridgeDomainConfigServiceException(String message) {
+        super(message);
+    }
+
+    public BridgeDomainConfigServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
+
index f8696b1..c841361 100644 (file)
@@ -29,7 +29,7 @@ public interface IPluginInBridgeDomainConfigService {
      * @note This method will return false if one or more of the supplied params is not supported by the
      * protocol plugin that serves the Node.
      */
-    public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws Throwable;
+    public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws BridgeDomainConfigServiceException;
 
     /**
      * Delete a Bridge Domain
index 64c7211..14c5e0d 100644 (file)
@@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
@@ -26,7 +27,7 @@ import org.slf4j.LoggerFactory;
 public class BridgeDomainConfigService implements IBridgeDomainConfigService {
     protected static final Logger logger = LoggerFactory
             .getLogger(BridgeDomainConfigService.class);
-    private ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
+    private final ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
             new ConcurrentHashMap<String, IPluginInBridgeDomainConfigService>();
 
     void setPluginInService (Map props, IPluginInBridgeDomainConfigService s) {
@@ -80,7 +81,7 @@ public class BridgeDomainConfigService implements IBridgeDomainConfigService {
 
     @Override
     public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params)
-            throws Throwable {
+            throws BridgeDomainConfigServiceException {
         if (pluginService != null) {
             IPluginInBridgeDomainConfigService plugin = this.pluginService.get(node.getType());
             if (plugin != null) {
index 1a71511..ed9374b 100644 (file)
@@ -13,9 +13,6 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 
 import org.apache.felix.dm.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
 import org.opendaylight.controller.hosttracker.IfIptoHost;
@@ -30,6 +27,8 @@ import org.opendaylight.controller.samples.simpleforwarding.IBroadcastPortSelect
 import org.opendaylight.controller.switchmanager.IInventoryListener;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class Activator extends ComponentActivatorAbstractBase {
     protected static final Logger logger = LoggerFactory
@@ -45,6 +44,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * instantiated in order to get an fully working implementation
      * Object
      */
+    @Override
     public Object[] getImplementations() {
         Object[] res = { SimpleForwardingImpl.class,
                          SimpleBroadcastHandlerImpl.class };
@@ -64,12 +64,17 @@ public class Activator extends ComponentActivatorAbstractBase {
      * also optional per-container different behavior if needed, usually
      * should not be the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
         if (imp.equals(SimpleForwardingImpl.class)) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put("salListenerName", "simpleforwarding");
+
             // export the service
             c.setInterface(new String[] { IInventoryListener.class.getName(),
                     IfNewHostNotify.class.getName(),
-                    IListenRoutingUpdates.class.getName() }, null);
+                    IListenRoutingUpdates.class.getName(),
+                    IListenDataPacket.class.getName() }, props);
 
             c.add(createContainerServiceDependency(containerName).setService(
                     IClusterContainerServices.class).setCallbacks(
@@ -96,7 +101,11 @@ public class Activator extends ComponentActivatorAbstractBase {
             c.add(createContainerServiceDependency(containerName).setService(
                     IRouting.class).setCallbacks("setRouting", "unsetRouting")
                     .setRequired(false));
-        }else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
+            c.add(createContainerServiceDependency(containerName).setService(
+                    IDataPacketService.class).setCallbacks("setDataPacketService",
+                   "unsetDataPacketService").setRequired(false));
+
+        } else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
             Dictionary<String, String> props = new Hashtable<String, String>();
             props.put("salListenerName", "simplebroadcasthandler");
 
index d2016b1..e39ae65 100644 (file)
@@ -12,12 +12,12 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import org.opendaylight.controller.sal.packet.Ethernet;
-import org.opendaylight.controller.sal.packet.IDataPacketService;
-import org.opendaylight.controller.sal.packet.IListenDataPacket;
 import org.opendaylight.controller.sal.core.ConstructionException;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
 import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.PacketResult;
 import org.opendaylight.controller.sal.packet.RawPacket;
@@ -54,7 +54,7 @@ public class SimpleBroadcastHandlerImpl implements IBroadcastHandler, IListenDat
     public PacketResult receiveDataPacket(RawPacket inPkt) {
         /*
          * note that this assumes that the protocol plugin will do appropriate
-         * filtering to ensure that this only receives packets for it's
+         * filtering to ensure that this only receives packets for its
          * container.
          */
 
@@ -216,6 +216,7 @@ public class SimpleBroadcastHandlerImpl implements IBroadcastHandler, IListenDat
         lock.writeLock().unlock();
     }
 
+    @Override
     public void setMode(BroadcastMode m) {
         lock.writeLock().lock();
         mode = m;
index 4b3363b..b9d8fee 100644 (file)
@@ -9,6 +9,7 @@
 
 package org.opendaylight.controller.samples.simpleforwarding.internal;
 
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -45,9 +46,17 @@ import org.opendaylight.controller.sal.core.UpdateType;
 import org.opendaylight.controller.sal.flowprogrammer.Flow;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
 import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
 import org.opendaylight.controller.sal.routing.IRouting;
 import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.samples.simpleforwarding.HostNodePair;
@@ -70,11 +79,10 @@ import org.slf4j.LoggerFactory;
  * installs those rules using <tt>installPerHostRules()</tt>.
  */
 public class SimpleForwardingImpl implements IfNewHostNotify,
-        IListenRoutingUpdates, IInventoryListener {
-    private static Logger log = LoggerFactory
-            .getLogger(SimpleForwardingImpl.class);
+        IListenRoutingUpdates, IInventoryListener, IListenDataPacket {
+    private static Logger log = LoggerFactory.getLogger(SimpleForwardingImpl.class);
     private static short DEFAULT_IPSWITCH_PRIORITY = 1;
-    private static String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
+    static final String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
     private IfIptoHost hostTracker;
     private IForwardingRulesManager frm;
     private ITopologyManager topologyManager;
@@ -90,6 +98,7 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
     private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
     private IClusterContainerServices clusterContainerService = null;
     private ISwitchManager switchManager;
+    private IDataPacketService dataPacketService;
 
     /**
      * Return codes from the programming of the perHost rules in HW
@@ -97,8 +106,19 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
     public enum RulesProgrammingReturnCode {
         SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
     }
+    public void setDataPacketService(IDataPacketService s) {
+        log.debug("Setting dataPacketService");
+        this.dataPacketService = s;
+    }
+
+    public void unsetDataPacketService(IDataPacketService s) {
+        if (this.dataPacketService == s) {
+            this.dataPacketService = null;
+        }
+    }
 
     public void setRouting(IRouting routing) {
+        log.debug("Setting routing");
         this.routing = routing;
     }
 
@@ -108,10 +128,6 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
         }
     }
 
-    public ITopologyManager getTopologyManager() {
-        return topologyManager;
-    }
-
     public void setTopologyManager(ITopologyManager topologyManager) {
         log.debug("Setting topologyManager");
         this.topologyManager = topologyManager;
@@ -670,8 +686,7 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
      *
      * @return a return code that convey the programming status of the HW
      */
-    private RulesProgrammingReturnCode uninstallPerHostRules(
-            HostNodeConnector host) {
+    private RulesProgrammingReturnCode uninstallPerHostRules(HostNodeConnector host) {
         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
         Map<NodeConnector, FlowEntry> pos;
         FlowEntry po;
@@ -768,16 +783,12 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
         for (Node swId : switches) {
             List<FlowEntry> pl = tobePrunedPos.get(swId);
             if (pl != null) {
-                log
-                        .debug(
-                                "Policies for Switch: {} in the list to be deleted: {}",
-                                swId, pl);
+                log.debug("Policies for Switch: {} in the list to be deleted: {}", swId, pl);
                 Iterator<FlowEntry> plIter = pl.iterator();
                 //for (Policy po: pl) {
                 while (plIter.hasNext()) {
                     FlowEntry po = plIter.next();
-                    log.error("Removing Policy, Switch: {} Policy: {}", swId,
-                            po);
+                    log.error("Removing Policy, Switch: {} Policy: {}", swId, po);
                     this.frm.uninstallFlowEntry(po);
                     plIter.remove();
                 }
@@ -962,4 +973,56 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
             this.switchManager = null;
         }
     }
+
+    @Override
+    public PacketResult receiveDataPacket(RawPacket inPkt) {
+        if (inPkt == null) {
+            return PacketResult.IGNORED;
+        }
+        log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
+        Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+        if (formattedPak instanceof Ethernet) {
+            Object nextPak = formattedPak.getPayload();
+            if (nextPak instanceof IPv4) {
+                log.trace("Handle punted IP packet: {}", formattedPak);
+                handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
+            }
+        }
+        return PacketResult.IGNORED;
+
+    }
+
+    private void handlePuntedIPPacket(IPv4 pkt, NodeConnector incomingNodeConnector) {
+        InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
+        if (dIP == null || hostTracker == null) {
+            log.debug("Invalid param(s) in handlePuntedIPPacket.. DestIP: {}. hostTracker: {}", dIP, hostTracker);
+            return;
+        }
+        HostNodeConnector destHost = hostTracker.hostFind(dIP);
+        if (destHost != null
+                && (routing == null ||
+                    routing.getRoute(incomingNodeConnector.getNode(), destHost.getnodeconnectorNode()) != null)) {
+
+            log.trace("Host {} is at {}", dIP, destHost.getnodeConnector());
+            HostNodePair key = new HostNodePair(destHost, incomingNodeConnector.getNode());
+
+            // If SimpleForwarding is aware of this host, it will try to install
+            // a path. Forward packet until it's done.
+            if (dataPacketService != null && this.rulesDB.containsKey(key)) {
+
+
+                /*
+                 * if we know where the host is and there's a path from where this
+                 * packet was punted to where the host is, then attempt best effort delivery to the host
+                 */
+                NodeConnector nc = destHost.getnodeConnector();
+                log.trace("Forwarding punted IP received at {} to {}", incomingNodeConnector, nc);
+                // re-encode the Ethernet packet (the parent of the IPv4 packet)
+                RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
+                rp.setOutgoingNodeConnector(nc);
+                this.dataPacketService.transmitDataPacket(rp);
+            }
+
+        }
+    }
 }
index e4bb790..a9f11fa 100644 (file)
@@ -774,7 +774,7 @@ public class Devices implements IDaylightWeb {
         return result;
     }
 
-    @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST)
+    @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,
@@ -811,7 +811,7 @@ public class Devices implements IDaylightWeb {
         return new Status(StatusCode.SUCCESS);
     }
 
-    @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST)
+    @RequestMapping(value = "/disconnect/{nodeId:.+}", method = RequestMethod.POST)
     @ResponseBody
     public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
             @RequestParam(required = true) String nodeType) {
index 259ffde..9c3c895 100644 (file)
@@ -14,11 +14,15 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 import javax.servlet.http.HttpServletRequest;
 
 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.SupportedFlowActions;
 import org.opendaylight.controller.sal.authorization.Privilege;
 import org.opendaylight.controller.sal.authorization.UserLevel;
 import org.opendaylight.controller.sal.core.Description;
@@ -212,7 +216,6 @@ public class Flows implements IDaylightWeb {
         return nodes;
     }
 
-
     @RequestMapping(value = "/flow", method = RequestMethod.POST)
     @ResponseBody
     public String actionFlow(@RequestParam(required = true) String action, @RequestParam(required = false) String body,
@@ -337,6 +340,83 @@ public class Flows implements IDaylightWeb {
         }
     }
 
+    @RequestMapping(value = "/valid-flows/{nodeId}")
+    @ResponseBody
+    public Object getValidActions(HttpServletRequest request, @RequestParam(required = false) String container,
+            @PathVariable("nodeId") String nodeId) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+
+        // Authorization check
+        String userName = request.getUserPrincipal().getName();
+        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+            return "Operation not authorized";
+        }
+
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        if (switchManager == null) {
+            return null;
+        }
+
+        Map<String, String> result = new TreeMap<String, String>();
+
+        Node node = Node.fromString(nodeId);
+        SupportedFlowActions supportedFlows = (SupportedFlowActions) switchManager.getNodeProp(node, "supportedFlowActions");
+        List<Class<? extends Action>> actions = supportedFlows.getActions();
+        for (Class<? extends Action> action : actions) {
+            if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Drop.class)) {
+                result.put(ActionType.DROP.toString(), "Drop");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Loopback.class)) {
+                result.put(ActionType.LOOPBACK.toString(), "Loopback");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Flood.class)) {
+                result.put(ActionType.FLOOD.toString(), "Flood");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.FloodAll.class)) {
+                result.put(ActionType.FLOOD_ALL.toString(), "Flood All");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Controller.class)) {
+                result.put(ActionType.CONTROLLER.toString(), "Controller");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SwPath.class)) {
+                result.put(ActionType.SW_PATH.toString(), "Software Path");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.HwPath.class)) {
+                result.put(ActionType.HW_PATH.toString(), "Hardware Path");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Output.class)) {
+                result.put(ActionType.OUTPUT.toString(), "Output");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Enqueue.class)) {
+                result.put(ActionType.ENQUEUE.toString(), "Enqueue");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlSrc.class)) {
+                result.put(ActionType.SET_DL_SRC.toString(), "Set Datalayer Source Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlDst.class)) {
+                result.put(ActionType.SET_DL_DST.toString(), "Set Datalayer Destination Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanId.class)) {
+                result.put(ActionType.SET_VLAN_ID.toString(), "Set VLAN ID");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanPcp.class)) {
+                result.put(ActionType.SET_VLAN_PCP.toString(), "Set VLAN Priority");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanCfi.class)) {
+                result.put(ActionType.SET_VLAN_CFI.toString(), "Set VLAN CFI");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PopVlan.class)) {
+                result.put(ActionType.POP_VLAN.toString(), "Pop VLAN");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PushVlan.class)) {
+                result.put(ActionType.PUSH_VLAN.toString(), "Push VLAN");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlType.class)) {
+                result.put(ActionType.SET_DL_TYPE.toString(), "Set EtherType");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwSrc.class)) {
+                result.put(ActionType.SET_NW_SRC.toString(), "Set Network Source Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwDst.class)) {
+                result.put(ActionType.SET_NW_DST.toString(), "Set Network Destination Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwTos.class)) {
+                result.put(ActionType.SET_NW_TOS.toString(), "Modify ToS Bits");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpSrc.class)) {
+                result.put(ActionType.SET_TP_SRC.toString(), "Modify Transport Source Port");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpDst.class)) {
+                result.put(ActionType.SET_TP_DST.toString(), "Modify Transport Destination Port");
+            }
+        }
+
+        return result;
+    }
+
+    private boolean actionCompare(String name, ActionType type) {
+        return name.equals(type.getId().toLowerCase());
+    }
+
     private String getNodeDesc(Node node, ISwitchManager switchManager) {
         Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
         String description = (desc == null) ? "" : desc.getValue();
index ab8301b..6e7fd25 100644 (file)
@@ -11,1593 +11,1723 @@ one.f = {};
 
 // specify dashlets and layouts
 one.f.dashlet = {
-    flows : {
-        id : 'flows',
-        name : 'Flow Entries'
-    },
-    nodes : {
-        id : 'nodes',
-        name : 'Nodes'
-    },
-    detail : {
-        id : 'detail',
-        name : 'Flow Detail'
-    }
+  flows : {
+    id : 'flows',
+    name : 'Flow Entries'
+  },
+  nodes : {
+    id : 'nodes',
+    name : 'Nodes'
+  },
+  detail : {
+    id : 'detail',
+    name : 'Flow Detail'
+  }
 };
 
 one.f.menu = {
-    left : {
-        top : [
-            one.f.dashlet.flows
-        ],
-        bottom : [
-            one.f.dashlet.nodes
-        ]
-    },
-    right : {
-        top : [],
-        bottom : [
-            one.f.dashlet.detail
-        ]
-    }
+  left : {
+    top : [
+      one.f.dashlet.flows
+      ],
+    bottom : [
+      one.f.dashlet.nodes
+      ]
+  },
+  right : {
+    top : [],
+    bottom : [
+      one.f.dashlet.detail
+      ]
+  }
 };
 
 one.f.address = {
-    root : "/controller/web/flows",
-    flows : {
-        main : "/main",
-        flows : "/node-flows",
-        nodes : "/node-ports",
-        flow : "/flow",
-        modifyFlow : "/modifyFlow",
-        deleteFlows:"/flow/deleteFlows"
-    }
+  root : "/controller/web/flows",
+  flows : {
+    main : "/main",
+    flows : "/node-flows",
+    nodes : "/node-ports",
+    flow : "/flow",
+    modifyFlow : "/modifyFlow",
+    deleteFlows:"/flow/deleteFlows"
+  }
 }
 
 /** NODES **/
 one.f.nodes = {
-    id : {
-        dashlet: {
-            datagrid: "one_f_nodes_id_dashlet_datagrid"
-        }
-    },
-    registry : {},
-    dashlet : function($dashlet) {
-        var $h4 = one.lib.dashlet.header("Nodes");
-        $dashlet.append($h4);
-
-        one.f.nodes.ajax.dashlet(function(data) {
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.nodes.data.nodesDataGrid(data);
-            $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
-        });
-    },
-    ajax : {
-        dashlet : function(callback) {
-            $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
-                callback(data);
-            });
-        }
-    },
-    data : {
-        nodesDataGrid: function(data) {
-            var gridData = [];
-            $.each(data, function(nodeName, flow) {
-                var nodeFlowObject = {};
-                nodeFlowObject["nodeName"] = nodeName;
-                nodeFlowObject["flows"] = flow;
-                nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
-                gridData.push(nodeFlowObject);
-            });
+  id : {
+    dashlet: {
+      datagrid: "one_f_nodes_id_dashlet_datagrid"
+    }
+  },
+  registry : {},
+  dashlet : function($dashlet) {
+    var $h4 = one.lib.dashlet.header("Nodes");
+    $dashlet.append($h4);
 
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'nodeName',
-                            label: 'Node',
-                            sortable: true
-                        },
-                        {
-                            property: 'flows',
-                            label: 'Flows',
-                            sortable: true
-                        }
-                    ],
-                    data: gridData,
-                    delay: 0
-                });
-            return source;
-        }
-    },
-    body : {
-        dashlet : function(body, callback) {
-            var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
+    one.f.nodes.ajax.dashlet(function(data) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.nodes.data.nodesDataGrid(data);
+      $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+    });
+  },
+  ajax : {
+    dashlet : function(callback) {
+      $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
+        callback(data);
+      });
+    }
+  },
+  data : {
+    nodesDataGrid: function(data) {
+      var gridData = [];
+      $.each(data, function(nodeName, flow) {
+        var nodeFlowObject = {};
+        nodeFlowObject["nodeName"] = nodeName;
+        nodeFlowObject["flows"] = flow;
+        nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
+        gridData.push(nodeFlowObject);
+      });
+
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'nodeName',
+          label: 'Node',
+          sortable: true
+      },
+          {
+            property: 'flows',
+          label: 'Flows',
+          sortable: true
+          }
+      ],
+          data: gridData,
+          delay: 0
+      });
+      return source;
+    }
+  },
+  body : {
+    dashlet : function(body, callback) {
+      var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
 
-            var headers = ['Node', 'Flows'];
-            var $thead = one.lib.dashlet.table.header(headers);
-            $table.append($thead);
+      var headers = ['Node', 'Flows'];
+      var $thead = one.lib.dashlet.table.header(headers);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        }
+      return $table;
     }
+  }
 }
 
 /** FLOW DETAIL **/
 one.f.detail = {
-    id : {},
-    registry : {},
-    dashlet : function($dashlet, details) {
-        var $h4 = one.lib.dashlet.header("Flow Details");
-        $dashlet.append($h4);
-
-        // details
-        if (details == undefined) {
-            var $none = $(document.createElement('div'));
-            $none.addClass('none');
-            var $p = $(document.createElement('p'));
-            $p.text('Please select a flow');
-            $p.addClass('text-center').addClass('text-info');
+  id : {},
+  registry : {},
+  dashlet : function($dashlet, details) {
+    var $h4 = one.lib.dashlet.header("Flow Details");
+    $dashlet.append($h4);
 
-            $dashlet.append($none)
-                .append($p);
-        }
-    },
-    data : {
-        dashlet : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-
-            entry.push(data['name']);
-            entry.push(data['node']);
-            entry.push(data['flow']['priority']);
-            entry.push(data['flow']['hardTimeout']);
-            entry.push(data['flow']['idleTimeout']);
-
-            tr.entry = entry;
-            body.push(tr);
-            return body;
-        },
-        description : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-            entry.push(data['flow']['ingressPort']);
-            entry.push(data['flow']['etherType']);
-            entry.push(data['flow']['vlanId']);
-            entry.push(data['flow']['vlanPriority']);
-            entry.push(data['flow']['srcMac']);
-            entry.push(data['flow']['dstMac']);
-            entry.push(data['flow']['srcIp']);
-            entry.push(data['flow']['dstIp']);
-            entry.push(data['flow']['tosBits']);
-            entry.push(data['flow']['srcPort']);
-            entry.push(data['flow']['dstPort']);
-            entry.push(data['flow']['protocol']);
-            entry.push(data['flow']['cookie']);
-
-            tr.entry = entry;
-            body.push(tr);
-            return body;
-        },
-        actions : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-            var actions = '';
-
-            $(data['flow']['actions']).each(function(index, value) {
-                var locEqualTo = value.indexOf("=");
-                if ( locEqualTo == -1 ) {
-                    actions += value + ', ';
-                } else {
-                    var action = value.substr(0,locEqualTo);
-                    if( action == "OUTPUT") {
-                        var portIds = value.substr(locEqualTo+1).split(",");
-                        actions += action + "=";
-                        var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
-                        for(var i =0; i < portIds.length ; i++) {
-                            var portName = allPorts[portIds[i]];
-                            actions += portName + ", ";
-                        }
-                    } else {
-                        actions += value + ', ';
-                    }
-                }
-            });
-            actions = actions.slice(0,-2);
-            entry.push(actions);
+    // details
+    if (details == undefined) {
+      var $none = $(document.createElement('div'));
+      $none.addClass('none');
+      var $p = $(document.createElement('p'));
+      $p.text('Please select a flow');
+      $p.addClass('text-center').addClass('text-info');
 
-            tr.entry = entry;
-            body.push(tr);
-            return body;
+      $dashlet.append($none)
+        .append($p);
+    }
+  },
+  data : {
+    dashlet : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+
+      entry.push(data['name']);
+      entry.push(data['node']);
+      entry.push(data['flow']['priority']);
+      entry.push(data['flow']['hardTimeout']);
+      entry.push(data['flow']['idleTimeout']);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    },
+    description : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+      entry.push(data['flow']['ingressPort']);
+      entry.push(data['flow']['etherType']);
+      entry.push(data['flow']['vlanId']);
+      entry.push(data['flow']['vlanPriority']);
+      entry.push(data['flow']['srcMac']);
+      entry.push(data['flow']['dstMac']);
+      entry.push(data['flow']['srcIp']);
+      entry.push(data['flow']['dstIp']);
+      entry.push(data['flow']['tosBits']);
+      entry.push(data['flow']['srcPort']);
+      entry.push(data['flow']['dstPort']);
+      entry.push(data['flow']['protocol']);
+      entry.push(data['flow']['cookie']);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    },
+    actions : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+      var actions = '';
+
+      $(data['flow']['actions']).each(function(index, value) {
+        var locEqualTo = value.indexOf("=");
+        if ( locEqualTo == -1 ) {
+          actions += value + ', ';
+        } else {
+          var action = value.substr(0,locEqualTo);
+          if( action == "OUTPUT") {
+            var portIds = value.substr(locEqualTo+1).split(",");
+            actions += action + "=";
+            var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
+            for(var i =0; i < portIds.length ; i++) {
+              var portName = allPorts[portIds[i]];
+              actions += portName + ", ";
+            }
+          } else {
+            actions += value + ', ';
+          }
         }
+      });
+      actions = actions.slice(0,-2);
+      entry.push(actions);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    }
+  },
+  body : {
+    dashlet : function(body) {
+      // create table
+      var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
+
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
+
+      return $table;
     },
-    body : {
-        dashlet : function(body) {
-            // create table
-            var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
-
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
-
-            return $table;
-        },
-        description : function(body) {
-            var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
+    description : function(body) {
+      var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        },
-        actions : function(body) {
-            var header = ['Actions'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
+      return $table;
+    },
+    actions : function(body) {
+      var header = ['Actions'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        }
+      return $table;
     }
+  }
 }
 
 /** FLOW ENTRIES **/
 one.f.flows = {
-    id : {
-        dashlet : {
-            add : "one_f_flows_id_dashlet_add",
-            removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
-            remove : "one_f_flows_id_dashlet_remove",
-            toggle : "one_f_flows_id_dashlet_toggle",
-            edit : "one_f_flows_id_dashlet_edit",
-            datagrid : "one_f_flows_id_dashlet_datagrid",
-            selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
-        },
+  id : {
+    dashlet : {
+      add : "one_f_flows_id_dashlet_add",
+      removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
+      remove : "one_f_flows_id_dashlet_remove",
+      toggle : "one_f_flows_id_dashlet_toggle",
+      edit : "one_f_flows_id_dashlet_edit",
+      datagrid : "one_f_flows_id_dashlet_datagrid",
+      selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
+    },
+    modal : {
+      install : "one_f_flows_id_modal_install",
+      edit : "one_f_flows_id_modal_edit",
+      add : "one_f_flows_id_modal_add",
+      close : "one_f_flows_id_modal_close",
+      modal : "one_f_flows_id_modal_modal",
+      dialog : {
+        modal : "one_f_flows_id_modal_dialog_modal",
+        remove : "one_f_flows_id_modal_dialog_remove",
+        close : "one_f_flows_id_modal_dialog_close"
+      },
+      action : {
+        button : "one_f_flows_id_modal_action_button",
+        modal : "one_f_flows_id_modal_action_modal",
+        add : "one_f_flows_id_modal_action_add",
+        close : "one_f_flows_id_modal_action_close",
+        table : "one_f_flows_id_modal_action_table",
+        addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
+        setVlanId : "one_f_flows_id_modal_action_setVlanId",
+        setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
+        modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
+        modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
+        modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
+        modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
+        modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
+        modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
+        modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
+        enqueue : 'one-f-flows-modal-action-enqueue',
+        queue : 'one-f-flows-modal-action-queue',
+        setEthertype : 'one-f-flows-modal-action-setEthertype',
+        pushVlan : 'one-f-flows-modal-action-pushVlan',
+        setVlanCfi : 'one-f-flows-modal-action-setVlanCfi',
         modal : {
-            install : "one_f_flows_id_modal_install",
-            edit : "one_f_flows_id_modal_edit",
-            add : "one_f_flows_id_modal_add",
-            close : "one_f_flows_id_modal_close",
-            modal : "one_f_flows_id_modal_modal",
-            dialog : {
-                modal : "one_f_flows_id_modal_dialog_modal",
-                remove : "one_f_flows_id_modal_dialog_remove",
-                close : "one_f_flows_id_modal_dialog_close"
-            },
-            action : {
-                button : "one_f_flows_id_modal_action_button",
-                modal : "one_f_flows_id_modal_action_modal",
-                add : "one_f_flows_id_modal_action_add",
-                close : "one_f_flows_id_modal_action_close",
-                table : "one_f_flows_id_modal_action_table",
-                addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
-                setVlanId : "one_f_flows_id_modal_action_setVlanId",
-                setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
-                modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
-                modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
-                modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
-                modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
-                modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
-                modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
-                modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
-                modal : {
-                    modal : "one_f_flows_modal_action_modal_modal",
-                    remove : "one_f_flows_modal_action_modal_remove",
-                    cancel : "one_f_flows_modal_action_modal_cancel"
-                }
-            },
-            form : {
-                name : "one_f_flows_id_modal_form_name",
-                nodes : "one_f_flows_id_modal_form_nodes",
-                port : "one_f_flows_id_modal_form_port",
-                priority : "one_f_flows_id_modal_form_priority",
-                hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
-                idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
-                cookie : "one_f_flows_id_modal_form_cookie",
-                etherType : "one_f_flows_id_modal_form_etherType",
-                vlanId : "one_f_flows_id_modal_form_vlanId",
-                vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
-                srcMac : "one_f_flows_id_modal_form_srcMac",
-                dstMac : "one_f_flows_id_modal_form_dstMac",
-                srcIp : "one_f_flows_id_modal_form_srcIp",
-                dstIp : "one_f_flows_id_modal_form_dstIp",
-                tosBits : "one_f_flows_id_modal_form_tosBits",
-                srcPort : "one_f_flows_id_modal_form_srcPort",
-                dstPort : "one_f_flows_id_modal_form_dstPort",
-                protocol : "one_f_flows_id_modal_form_protocol"
-            }
+          modal : "one_f_flows_modal_action_modal_modal",
+          remove : "one_f_flows_modal_action_modal_remove",
+          cancel : "one_f_flows_modal_action_modal_cancel"
         }
-    },
-    registry : {},
-    dashlet : function($dashlet, callback) {
-
-        // load body
-        one.f.flows.ajax.dashlet(function(data) {
-
-            var $h4 = one.lib.dashlet.header("Flow Entries");
-
-            $dashlet.append($h4);
-            if (one.f.flows.registry.privilege === 'WRITE') {
-                var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-
-                $button.click(function() {
-                    var $modal = one.f.flows.modal.initialize();
-                    $modal.modal();
-                });
-                $dashlet.append($button);
-                var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-
-                $button.click(function() {
-                    var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
-                    if (checkedCheckBoxes.size() === 0) {
-                        return false;
-                    }
-                    
-                    var requestData = [];
-                    
-                    checkedCheckBoxes.each(function(index, value) {
-                        var flowEntry = {};
-                        flowEntry['name'] = checkedCheckBoxes[index].name;
-                        flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
-                        requestData.push(flowEntry);  
-                    });
-                    one.f.flows.modal.removeMultiple.dialog(requestData);
-                });
-                $dashlet.append($button);
+      },
+      form : {
+        name : "one_f_flows_id_modal_form_name",
+        nodes : "one_f_flows_id_modal_form_nodes",
+        port : "one_f_flows_id_modal_form_port",
+        priority : "one_f_flows_id_modal_form_priority",
+        hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
+        idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
+        cookie : "one_f_flows_id_modal_form_cookie",
+        etherType : "one_f_flows_id_modal_form_etherType",
+        vlanId : "one_f_flows_id_modal_form_vlanId",
+        vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
+        srcMac : "one_f_flows_id_modal_form_srcMac",
+        dstMac : "one_f_flows_id_modal_form_dstMac",
+        srcIp : "one_f_flows_id_modal_form_srcIp",
+        dstIp : "one_f_flows_id_modal_form_dstIp",
+        tosBits : "one_f_flows_id_modal_form_tosBits",
+        srcPort : "one_f_flows_id_modal_form_srcPort",
+        dstPort : "one_f_flows_id_modal_form_dstPort",
+        protocol : "one_f_flows_id_modal_form_protocol",
+        action : 'one-f-flows-id-modal-form-action'
+      }
+    }
+  },
+  registry : {},
+  dashlet : function($dashlet, callback) {
+    // load body
+    one.f.flows.ajax.dashlet(function(data) {
+      var $h4 = one.lib.dashlet.header("Flow Entries");
+      $dashlet.append($h4);
+      if (one.f.flows.registry.privilege === 'WRITE') {
+        var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+
+        $button.click(function() {
+          var $modal = one.f.flows.modal.initialize();
+          $modal.modal();
+        });
+        $dashlet.append($button);
+        var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+
+        $button.click(function() {
+          var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+          if (checkedCheckBoxes.size() === 0) {
+            return false;
+          }
 
-            }
+          var requestData = [];
 
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.flows.data.flowsDataGrid(data);
-            $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
-                    $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
-                                $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
-                                        $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
-                    });
-                    
-                    $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
-                    $tr = $(tr);
-                    $span = $("td span", $tr);
-                    var flowstatus = $span.data("flowstatus");
-                    if($span.data("installinhw") != null) {
-                        var installInHw = $span.data("installinhw").toString();
-                        if(installInHw == "true" && flowstatus == "Success") {
-                            $tr.addClass("success");
-                        } else {
-                            $tr.addClass("warning");
-                        }
-                    }
-                    // attach mouseover to show pointer cursor
-                    $tr.mouseover(function() {
-                        $(this).css("cursor", "pointer");
-                    });
-                    // attach click event
-                    $tr.click(function() {
-                        var $td = $($(this).find("td")[1]);
-                        var id = $td.text();
-                        var node = $td.find("span").data("nodeid");
-                        one.f.flows.detail(id, node);
-                    });
-                    $(".flowEntry").click(function(e){
-                                if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
-                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
-                                .prop("checked",
-                              true);
-                        } else {
-                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
-                                .prop("checked",
-                             false);
-                        }
-                        e.stopPropagation();
-                    });
-                });
-            });
-            
-            // details callback
-            if(callback != undefined) callback();
+          checkedCheckBoxes.each(function(index, value) {
+            var flowEntry = {};
+            flowEntry['name'] = checkedCheckBoxes[index].name;
+            flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+            requestData.push(flowEntry);  
+          });
+          one.f.flows.modal.removeMultiple.dialog(requestData);
         });
-    },
-    detail : function(id, node) {
-        // clear flow details
-        var $detailDashlet = one.main.dashlet.right.bottom;
-        $detailDashlet.empty();
-        var $h4 = one.lib.dashlet.header("Flow Overview");
-        $detailDashlet.append($h4);
-
-        // details
-        var flows = one.f.flows.registry.flows;
-        one.f.flows.registry['selectedId'] = id;
-        one.f.flows.registry['selectedNode'] = node;
-        var flow;
-        $(flows).each(function(index, value) {
-          if (value.name == id && value.nodeId == node) {
-            flow = value;
-          }
+        $dashlet.append($button);
+
+      }
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.flows.data.flowsDataGrid(data);
+      $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+        $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
+          $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
+            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
         });
-        if (one.f.flows.registry.privilege === 'WRITE') {
-            // remove button
-            var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
-            var $button = one.lib.dashlet.button.button(button);
-            $button.click(function() {
-                var $modal = one.f.flows.modal.dialog.initialize(id, node);
-                $modal.modal();
-            });
-            // edit button
-            var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
-            var $editButton = one.lib.dashlet.button.button(editButton);
-            $editButton.click(function() {
-               var install = flow['flow']['installInHw'];
-                var $modal = one.f.flows.modal.initialize(true,install);
-                $modal.modal().on('shown',function(){
-                    var $port = $('#'+one.f.flows.id.modal.form.port);
-                    $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
-                });
-            });
-            // toggle button
-            var toggle;
-            if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
-                toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
-            } else {
-                toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
-            }
-            var $toggle = one.lib.dashlet.button.button(toggle);
-            $toggle.click(function() {
-                one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
-                    if(data == "Success") {
-                        one.main.dashlet.right.bottom.empty();
-                        one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top, function() {
-                           // checks are backwards due to stale registry
-                           if(flow['flow']['installInHw'] == 'true') {
-                               one.lib.alert('Uninstalled Flow');
-                           } else {
-                               one.lib.alert('Installed Flow');
-                           }
-                           one.f.flows.detail(id, node)
-                        });
-                    } else {
-                        one.lib.alert('Cannot toggle flow: '+data);
-                    }
-                });
-            });
 
-            $detailDashlet.append($button).append($editButton).append($toggle);
-        }
-        // append details
-        var body = one.f.detail.data.dashlet(flow);
-        var $body = one.f.detail.body.dashlet(body);
-        $detailDashlet.append($body);
-        var body = one.f.detail.data.description(flow);
-        var $body = one.f.detail.body.description(body);
-        $detailDashlet.append($body);
-        var body = one.f.detail.data.actions(flow);
-        var $body = one.f.detail.body.actions(body);
-        $detailDashlet.append($body);
-    },
-    modal : {
-        dialog : {
-            initialize : function(id, node) {
-                var h3 = "Remove Flow";
-                var $p = one.f.flows.modal.dialog.body(id);
-                var footer = one.f.flows.modal.dialog.footer();
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
-                $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-                $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
-                    one.f.flows.modal.ajax.removeflow(id, node, function(data) {
-                        if (data == "Success") {
-                            $modal.modal('hide');
-                            one.main.dashlet.right.bottom.empty();
-                            one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                            one.main.dashlet.left.top.empty();
-                            one.f.flows.dashlet(one.main.dashlet.left.top);
-                            one.lib.alert('Flow removed');
-                        } else {
-                            one.lib.alert('Cannot remove flow: '+data);
-                        }
-                    });
-                });
-                return $modal;
-            },
-            footer : function() {
-                var footer = [];
-
-                var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
-                var $removeButton = one.lib.dashlet.button.button(removeButton);
-                footer.push($removeButton);
-
-                var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
-                var $closeButton = one.lib.dashlet.button.button(closeButton);
-                footer.push($closeButton);
-
-                return footer;
-            },
-            body : function(id) {
-                var $p = $(document.createElement('p'));
-                $p.append('Remove flow '+id+'?');
-                return $p;
+        $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
+          $tr = $(tr);
+          $span = $("td span", $tr);
+          var flowstatus = $span.data("flowstatus");
+          if($span.data("installinhw") != null) {
+            var installInHw = $span.data("installinhw").toString();
+            if(installInHw == "true" && flowstatus == "Success") {
+              $tr.addClass("success");
+            } else {
+              $tr.addClass("warning");
             }
-        },
-        initialize : function(edit,install) {
-            var h3;
-            if(edit) {
-                h3 = "Edit Flow Entry";
-                var footer = one.f.flows.modal.footerEdit();
-
+          }
+          // attach mouseover to show pointer cursor
+          $tr.mouseover(function() {
+            $(this).css("cursor", "pointer");
+          });
+          // attach click event
+          $tr.click(function() {
+            var $td = $($(this).find("td")[1]);
+            var id = $td.text();
+            var node = $td.find("span").data("nodeid");
+            one.f.flows.detail(id, node);
+          });
+          $(".flowEntry").click(function(e){
+            if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
+              $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+            .prop("checked",
+              true);
             } else {
-                h3 = "Add Flow Entry";
-                var footer = one.f.flows.modal.footer();
+              $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+            .prop("checked",
+              false);
             }
-
-            var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
-
-            // bind close button
-            $('#'+one.f.flows.id.modal.close, $modal).click(function() {
-                $modal.modal('hide');
+            e.stopPropagation();
+          });
+        });
+      });
+
+      // details callback
+      if(callback != undefined) callback();
+    });
+  },
+  detail : function(id, node) {
+    // clear flow details
+    var $detailDashlet = one.main.dashlet.right.bottom;
+    $detailDashlet.empty();
+    var $h4 = one.lib.dashlet.header("Flow Overview");
+    $detailDashlet.append($h4);
+
+    // details
+    var flows = one.f.flows.registry.flows;
+    one.f.flows.registry['selectedId'] = id;
+    one.f.flows.registry['selectedNode'] = node;
+    var flow;
+    $(flows).each(function(index, value) {
+      if (value.name == id && value.nodeId == node) {
+        flow = value;
+      }
+    });
+    if (one.f.flows.registry.privilege === 'WRITE') {
+      // remove button
+      var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
+      var $button = one.lib.dashlet.button.button(button);
+      $button.click(function() {
+        var $modal = one.f.flows.modal.dialog.initialize(id, node);
+        $modal.modal();
+      });
+      // edit button
+      var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
+      var $editButton = one.lib.dashlet.button.button(editButton);
+      $editButton.click(function() {
+        var install = flow['flow']['installInHw'];
+        var $modal = one.f.flows.modal.initialize(true,install);
+        $modal.modal().on('shown',function(){
+          var $port = $('#'+one.f.flows.id.modal.form.port);
+          $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
+        });
+      });
+      // toggle button
+      var toggle;
+      if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
+        toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
+      } else {
+        toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
+      }
+      var $toggle = one.lib.dashlet.button.button(toggle);
+      $toggle.click(function() {
+        one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
+          if(data == "Success") {
+            one.main.dashlet.right.bottom.empty();
+            one.f.detail.dashlet(one.main.dashlet.right.bottom);
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top, function() {
+              // checks are backwards due to stale registry
+              if(flow['flow']['installInHw'] == 'true') {
+                one.lib.alert('Uninstalled Flow');
+              } else {
+                one.lib.alert('Installed Flow');
+              }
+              one.f.flows.detail(id, node)
             });
+          } else {
+            one.lib.alert('Cannot toggle flow: '+data);
+          }
+        });
+      });
 
-            if (edit) {
-                // bind edit flow button
-                $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
-                    one.f.flows.modal.save($modal, install, true);
-                });
+      $detailDashlet.append($button).append($editButton).append($toggle);
+    }
+    // append details
+    var body = one.f.detail.data.dashlet(flow);
+    var $body = one.f.detail.body.dashlet(body);
+    $detailDashlet.append($body);
+    var body = one.f.detail.data.description(flow);
+    var $body = one.f.detail.body.description(body);
+    $detailDashlet.append($body);
+    var body = one.f.detail.data.actions(flow);
+    var $body = one.f.detail.body.actions(body);
+    $detailDashlet.append($body);
+  },
+  modal : {
+    dialog : {
+      initialize : function(id, node) {
+        var h3 = "Remove Flow";
+        var $p = one.f.flows.modal.dialog.body(id);
+        var footer = one.f.flows.modal.dialog.footer();
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
+        $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
+          one.f.flows.modal.ajax.removeflow(id, node, function(data) {
+            if (data == "Success") {
+              $modal.modal('hide');
+              one.main.dashlet.right.bottom.empty();
+              one.f.detail.dashlet(one.main.dashlet.right.bottom);
+              one.main.dashlet.left.top.empty();
+              one.f.flows.dashlet(one.main.dashlet.left.top);
+              one.lib.alert('Flow removed');
             } else {
-                // bind add flow button
-                $('#'+one.f.flows.id.modal.add, $modal).click(function() {
-                    one.f.flows.modal.save($modal, 'false');
-                });
-
-                // bind install flow button
-                $('#'+one.f.flows.id.modal.install, $modal).click(function() {
-                    one.f.flows.modal.save($modal, 'true');
-                });
+              one.lib.alert('Cannot remove flow: '+data);
             }
+          });
+        });
+        return $modal;
+      },
+      footer : function() {
+        var footer = [];
+
+        var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
+        var $removeButton = one.lib.dashlet.button.button(removeButton);
+        footer.push($removeButton);
+
+        var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
+        var $closeButton = one.lib.dashlet.button.button(closeButton);
+        footer.push($closeButton);
+
+        return footer;
+      },
+      body : function(id) {
+        var $p = $(document.createElement('p'));
+        $p.append('Remove flow '+id+'?');
+        return $p;
+      }
+    },
+    initialize : function(edit,install) {
+      var h3;
+      if(edit) {
+        h3 = "Edit Flow Entry";
+        var footer = one.f.flows.modal.footerEdit();
+
+      } else {
+        h3 = "Add Flow Entry";
+        var footer = one.f.flows.modal.footer();
+      }
+
+      var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
+
+      // bind close button
+      $('#'+one.f.flows.id.modal.close, $modal).click(function() {
+        $modal.modal('hide');
+      });
+
+      if (edit) {
+        // bind edit flow button
+        $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
+          one.f.flows.modal.save($modal, install, true);
+        });
+      } else {
+        // bind add flow button
+        $('#'+one.f.flows.id.modal.add, $modal).click(function() {
+          one.f.flows.modal.save($modal, 'false');
+        });
 
+        // bind install flow button
+        $('#'+one.f.flows.id.modal.install, $modal).click(function() {
+          one.f.flows.modal.save($modal, 'true');
+        });
+      }
 
-            var nodes = one.f.flows.registry.nodes;
-            var nodeports = one.f.flows.registry.nodeports;
-            var $body = one.f.flows.modal.body(nodes, nodeports, edit);
-            one.lib.modal.inject.body($modal, $body,edit);
-
-            return $modal;
-        },
-        save : function($modal, install, edit) {
-            var result = {};
-
-            result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
-            result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
-            result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
-            result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
-            result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
-            result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
-            result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
-            result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
-            result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
-            result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
-            result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
-            result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
-            result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
-            result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
-            result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
-            result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
-            result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
-            result['installInHw'] = install;
-
-            var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
-
-            $.each(result, function(key, value) {
-                if (value == "") delete result[key];
-            });
+      var nodes = one.f.flows.registry.nodes;
+      var nodeports = one.f.flows.registry.nodeports;
+      var $body = one.f.flows.modal.body(nodes, nodeports, edit);
+      one.lib.modal.inject.body($modal, $body,edit);
 
-            var action = [];
-            var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
-            $($table.find('tbody').find('tr')).each(function(index, value) {
-                if (!$(this).find('td').hasClass('empty')) {
-                    action.push($(value).data('action'));
-                }
+      return $modal;
+    },
+    save : function($modal, install, edit) {
+      var result = {};
+
+      result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
+      result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
+      result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
+      result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
+      result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
+      result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
+      result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
+      result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
+      result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
+      result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
+      result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
+      result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
+      result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
+      result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
+      result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
+      result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
+      result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
+      result['installInHw'] = install;
+
+      var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
+
+      $.each(result, function(key, value) {
+        if (value == "") delete result[key];
+      });
+
+      var action = [];
+      var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
+      $($table.find('tbody').find('tr')).each(function(index, value) {
+        if (!$(this).find('td').hasClass('empty')) {
+          action.push($(value).data('action'));
+        }
+      });
+      result['actions'] = action;
+
+      // frontend validation
+      if (result['name'] == undefined) {
+        alert('Need flow name');
+        return;
+      }
+      if (nodeId == '') {
+        alert('Select node');
+        return;
+      }
+      if (action.length == 0) {
+        alert('Please specify an action');
+        return;
+      }
+
+      // package for ajax call
+      var resource = {};
+      resource['body'] = JSON.stringify(result);
+      if(edit){
+        resource['action'] = 'edit';
+      } else {
+        resource['action'] = 'add';
+      }
+
+      resource['nodeId'] = nodeId;
+
+      if (edit) {
+        one.f.flows.modal.ajax.saveflow(resource, function(data) {
+          if (data == "Success") {
+            $modal.modal('hide').on('hidden', function () {
+              one.f.flows.detail(result['name'], nodeId);
             });
-            result['actions'] = action;
-
-            // frontend validation
-            if (result['name'] == undefined) {
-                alert('Need flow name');
-                return;
-            }
-            if (nodeId == '') {
-                alert('Select node');
-                return;
-            }
-            if (action.length == 0) {
-                alert('Please specify an action');
-                return;
-            }
-
-            // package for ajax call
-            var resource = {};
-            resource['body'] = JSON.stringify(result);
-            if(edit){
-                resource['action'] = 'edit';
+            one.lib.alert('Flow Entry edited');
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          } else {
+            alert('Could not edit flow: '+data);
+          }
+        });
+      } else {
+        one.f.flows.modal.ajax.saveflow(resource, function(data) {
+          if (data == "Success") {
+            $modal.modal('hide');
+            one.lib.alert('Flow Entry added');
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          } else {
+            alert('Could not add flow: '+data);
+          }
+        });
+      }
+    },
+    ajax : {
+      nodes : function(successCallback) {
+        $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
+          var nodes = one.f.flows.modal.data.nodes(data);
+          var nodeports = data;
+          one.f.flows.registry['nodes'] = nodes;
+          one.f.flows.registry['nodeports'] = nodeports;
+
+          successCallback(nodes, nodeports);
+        });
+      },
+      saveflow : function(resource, callback) {
+        $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
+          callback(data);
+        });
+      },
+      removeflow : function(id, node, callback) {
+        resource = {};
+        resource['action'] = 'remove';
+        $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+          callback(data);
+        });
+      },
+      toggleflow : function(id, node, callback) {
+        resource = {};
+        resource['action'] = 'toggle';
+        $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+          callback(data);
+        });
+      }
+    },
+    data : {
+      nodes : function(data) {
+        result = {};
+        $.each(data, function(key, value) {
+          result[key] = value['name'];
+        });
+        return result;
+      }
+    },
+    body : function(nodes, nodeports, edit) {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      var existingFlow;
+      // flow description
+      var $legend = one.lib.form.legend("");
+      $legend.css('visibility', 'hidden');
+      $fieldset.append($legend);
+      // name
+      var $label = one.lib.form.label("Name");
+      var $input = one.lib.form.input("Flow Name");
+      $input.attr('id', one.f.flows.id.modal.form.name);
+      if(edit) {
+        $input.attr('disabled', 'disabled');
+        var flows = one.f.flows.registry.flows;
+        $(flows).each(function(index, value) {
+          if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
+            existingFlow = value.flow;
+          }
+        });
+        $input.val(existingFlow.name);
+      }
+
+      $fieldset.append($label).append($input);
+      // 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.flows.id.modal.form.nodes);
+      if(edit) {
+        $select.attr('disabled', 'disabled');
+        $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
+      }
+
+      // bind onchange
+      $select.change(function() {
+        // retrieve port value
+        var node = $(this).find('option:selected').attr('value');
+        var $ports = $('#'+one.f.flows.id.modal.form.port);
+        if (node == '') {
+          one.lib.form.select.inject($ports, {});
+          return;
+        }
+        one.f.flows.registry['currentNode'] = node;
+        var ports = nodeports[node]['ports'];
+        one.lib.form.select.inject($ports, ports);
+        one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+        $ports.val($ports.find("option:first").val());
+        if(edit) {
+          $ports.val( existingFlow.ingressPort );
+        }
+        $.getJSON(one.f.address.root+'/valid-flows/'+node, function(response) {
+          var $select = $('#'+one.f.flows.id.modal.form.action, $fieldset);
+          one.lib.form.select.inject($select, response);
+          one.lib.form.select.prepend($select, {'' : 'Please Select an Action'});
+          // when selecting an action
+          $select.change(function() {
+            var action = $(this).find('option:selected');
+            one.f.flows.modal.action.parse(action.attr('value'));
+            $select[0].selectedIndex = 0;
+          });
+        });
+      });
+
+      $fieldset.append($label).append($select);
+      // input port
+      var $label = one.lib.form.label("Input Port");
+      var $select = one.lib.form.select.create();
+
+      $select.attr('id', one.f.flows.id.modal.form.port);
+      $fieldset.append($label).append($select);
+      // priority
+      var $label = one.lib.form.label("Priority");
+      var $input = one.lib.form.input("Priority");
+      $input.attr('id', one.f.flows.id.modal.form.priority);
+      $input.val('500');
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.priority);
+      }
+      // hardTimeout
+      var $label = one.lib.form.label("Hard Timeout");
+      var $input = one.lib.form.input("Hard Timeout");
+      $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
+      if(edit) {
+        $input.val(existingFlow.hardTimeout);
+      }
+      $fieldset.append($label).append($input);
+
+      // idleTimeout
+      var $label = one.lib.form.label("Idle Timeout");
+      var $input = one.lib.form.input("Idle Timeout");
+      $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.idleTimeout);
+      }
+      // cookie
+      var $label = one.lib.form.label("Cookie");
+      var $input = one.lib.form.input("Cookie");
+      $input.attr('id', one.f.flows.id.modal.form.cookie);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.cookie);
+      }
+
+      // layer 2
+      var $legend = one.lib.form.legend("Layer 2");
+      $fieldset.append($legend);
+      // etherType
+      var $label = one.lib.form.label("Ethernet Type");
+      var $input = one.lib.form.input("Ethernet Type");
+      $input.attr('id', one.f.flows.id.modal.form.etherType);
+      $input.val('0x800');
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.etherType);
+      }
+      // vlanId
+      var $label = one.lib.form.label("VLAN Identification Number");
+      var $input = one.lib.form.input("VLAN Identification Number");
+      $input.attr('id', one.f.flows.id.modal.form.vlanId);
+      var $help = one.lib.form.help("Range: 0 - 4095");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.vlanId);
+      }
+
+      // vlanPriority
+      var $label = one.lib.form.label("VLAN Priority");
+      var $input = one.lib.form.input("VLAN Priority");
+      $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
+      var $help = one.lib.form.help("Range: 0 - 7");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.vlanPriority);
+      }
+
+      // srcMac
+      var $label = one.lib.form.label("Source MAC Address");
+      var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
+      $input.attr('id', one.f.flows.id.modal.form.srcMac);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.srcMac);
+      }
+      // dstMac
+      var $label = one.lib.form.label("Destination MAC Address");
+      var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
+      $input.attr('id', one.f.flows.id.modal.form.dstMac);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.dstMac);
+      }
+      // layer 3
+      var $legend = one.lib.form.legend("Layer 3");
+      $fieldset.append($legend);
+
+      // srcIp
+      var $label = one.lib.form.label("Source IP Address");
+      var $input = one.lib.form.input("192.168.3.128");
+      $input.attr('id', one.f.flows.id.modal.form.srcIp);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.srcIp);
+      }
+      // dstIp
+      var $label = one.lib.form.label("Destination IP Address");
+      var $input = one.lib.form.input("2001:2334::0/32");
+      $input.attr('id', one.f.flows.id.modal.form.dstIp);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.dstIp);
+      }
+      // tosBits
+      var $label = one.lib.form.label("ToS Bits");
+      var $input = one.lib.form.input("ToS Bits");
+      $input.attr('id', one.f.flows.id.modal.form.tosBits);
+      var $help = one.lib.form.help("Range: 0 - 63");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.tosBits);
+      }
+
+      // layer 4
+      var $legend = one.lib.form.legend("Layer 4");
+      $fieldset.append($legend);
+      // srcPort
+      var $label = one.lib.form.label("Source Port");
+      var $input = one.lib.form.input("Source Port");
+      $input.attr('id', one.f.flows.id.modal.form.srcPort);
+      var $help = one.lib.form.help("Range: 0 - 65535");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.srcPort);
+      }
+      // dstPort
+      var $label = one.lib.form.label("Destination Port");
+      var $input = one.lib.form.input("Destination Port");
+      $input.attr('id', one.f.flows.id.modal.form.dstPort);
+      var $help = one.lib.form.help("Range: 0 - 65535");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.dstPort);
+      }
+      // protocol
+      var $label = one.lib.form.label("Protocol");
+      var $input = one.lib.form.input("Protocol");
+      $input.attr('id', one.f.flows.id.modal.form.protocol);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.protocol);
+      }
+      // actions
+      var $legend = one.lib.form.label("Actions");
+      $fieldset.append($legend);
+      // actions table
+      var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
+      var $table = one.lib.dashlet.table.table(tableAttributes);
+      $table.attr('id', one.f.flows.id.modal.action.table);
+      var tableHeaders = ["Action", "Data"];
+      var $thead = one.lib.dashlet.table.header(tableHeaders);
+      var $tbody = one.lib.dashlet.table.body("", tableHeaders);
+      $table.append($thead).append($tbody);
+      // actions
+      var actions = {
+        "" : "Please Select an Action",
+        "DROP" : "Drop",
+        "LOOPBACK" : "Loopback",
+        "FLOOD" : "Flood",
+        "FLOOD_ALL" : "Flood All",
+        "CONTROLLER" : "Controller",
+        "SW_PATH" : "Software Path",
+        "HW_PATH" : "Hardware Path",
+        "OUTPUT" : "Add Output Ports",
+        "ENQUEUE" : "Enqueue",
+        "SET_VLAN_ID" : "Set VLAN ID",
+        "SET_VLAN_PCP" : "Set VLAN Priority",
+        "SET_VLAN_CFI" : "Set VLAN CFI",
+        "POP_VLAN" : "Strip VLAN Header",
+        "PUSH_VLAN" : "Push VLAN",
+        "SET_DL_SRC" : "Modify Datalayer Source Address",
+        "SET_DL_DST" : "Modify Datalayer Destination Address",
+        "SET_DL_TYPE" : "Set Ethertype",
+        "SET_NW_SRC" : "Modify Network Source Address",
+        "SET_NW_DST" :"Modify Network Destination Address",
+        "SET_NW_TOS" : "Modify ToS Bits",
+        "SET_TP_SRC" : "Modify Transport Source Port",
+        "SET_TP_DST" : "Modify Transport Destination Port"
+      };
+      var $select = one.lib.form.select.create(actions);
+      $select.attr('id', one.f.flows.id.modal.form.action);
+      // when selecting an action
+      $select.change(function() {
+        var action = $(this).find('option:selected');
+        one.f.flows.modal.action.parse(action.attr('value'));
+        $select[0].selectedIndex = 0;
+      });
+
+      if(edit) {
+        $(existingFlow.actions).each(function(index, value){
+          setTimeout(function(){
+            var locEqualTo = value.indexOf("=");
+            if ( locEqualTo == -1 ) {
+              one.f.flows.modal.action.add.add(actions[value], value);
             } else {
-                resource['action'] = 'add';
+              var action = value.substr(0,locEqualTo);
+              if( action == "OUTPUT") {
+                var portIds = value.substr(locEqualTo+1).split(",");
+                var ports = [];
+                var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+                for(var i =0; i < portIds.length ; i++) {
+                  var portName = allPorts[portIds[i]];
+                  ports.push(portName);
+                }
+                one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
+              } else {
+                var val = value.substr(locEqualTo+1);
+                one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
+              }
             }
+          }, 1000)
+        });
+      }
+      $fieldset.append($select).append($table);
 
-            resource['nodeId'] = nodeId;
-
-            if (edit) {
-                    one.f.flows.modal.ajax.saveflow(resource, function(data) {
-                    if (data == "Success") {
-                        $modal.modal('hide').on('hidden', function () {
-                            one.f.flows.detail(result['name'], nodeId);
-                        });
-                        one.lib.alert('Flow Entry edited');
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    } else {
-                        alert('Could not edit flow: '+data);
-                    }
-                });
-            } else {
-                    one.f.flows.modal.ajax.saveflow(resource, function(data) {
-                    if (data == "Success") {
-                        $modal.modal('hide');
-                        one.lib.alert('Flow Entry added');
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    } else {
-                        alert('Could not add flow: '+data);
-                    }
-                });
-            }
+      // return
+      $form.append($fieldset);
+      return $form;
+    },
+    action : {
+      parse : function(option) {
+        switch (option) {
+          case "OUTPUT" :
+            var h3 = "Add Output Port";
+            var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
+            $modal.modal();
+            break;
+          case "SET_VLAN_ID" :
+            var h3 = "Set VLAN ID";
+            var placeholder = "VLAN Identification Number";
+            var id = one.f.flows.id.modal.action.setVlanId;
+            var help = "Range: 0 - 4095";
+            var action = 'SET_VLAN_ID';
+            var name = "VLAN ID";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_VLAN_PCP" :
+            var h3 = "Set VLAN Priority";
+            var placeholder = "VLAN Priority";
+            var id = one.f.flows.id.modal.action.setVlanPriority;
+            var help = "Range: 0 - 7";
+            var action = 'SET_VLAN_PCP';
+            var name = "VLAN Priority";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_VLAN_CFI" :
+            var h3 = "Set VLAN CFI";
+            var placeholder = "VLAN CFI";
+            var id = one.f.flows.id.modal.action.setVlanCfi;
+            var help = "Range: 0 - 1";
+            var action = 'SET_VLAN_CFI';
+            var name = "VLAN CFI";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "POP_VLAN" :
+            var name = "Strip VLAN Header";
+            var action = 'POP_VLAN';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "PUSH_VLAN" :
+            var h3 = "Push VLAN";
+            var placeholder = "VLAN";
+            var id = one.f.flows.id.modal.action.pushVlan;
+            var help = "Range: 0 - 4095";
+            var action = 'PUSH_VLAN';
+            var name = "VLAN";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_SRC" :
+            var h3 = "Set Source MAC Address";
+            var placeholder = "Source MAC Address";
+            var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
+            var help = "Example: 00:11:22:aa:bb:cc";
+            var action = 'SET_DL_SRC';
+            var name = "Source MAC";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_DST" :
+            var h3 = "Set Destination MAC Address";
+            var placeholder = "Destination MAC Address";
+            var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
+            var help = "Example: 00:11:22:aa:bb:cc";
+            var action = 'SET_DL_DST';
+            var name = "Destination MAC";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_TYPE" :
+            var h3 = "Set Ethertype";
+            var placeholder = "Ethertype";
+            var id = one.f.flows.id.modal.action.setEthertype;
+            var help = "Range: 0 - 65535";
+            var action = 'SET_DL_TYPE';
+            var name = "Ethertype";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_SRC" :
+            var h3 = "Set IP Source Address";
+            var placeholder = "Source IP Address";
+            var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
+            var help = "Example: 127.0.0.1";
+            var action = 'SET_NW_SRC';
+            var name = "Source IP";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_DST" :
+            var h3 = "Set IP Destination Address";
+            var placeholder = "Destination IP Address";
+            var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
+            var help = "Example: 127.0.0.1";
+            var action = 'SET_NW_DST';
+            var name = "Destination IP";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_TOS" :
+            var h3 = "Set IPv4 ToS";
+            var placeholder = "IPv4 ToS";
+            var id = one.f.flows.id.modal.action.modifyTosBits;
+            var help = "Range: 0 - 63";
+            var action = 'SET_NW_TOS';
+            var name = "ToS Bits";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_TP_SRC" :
+            var h3 = "Set Transport Source Port";
+            var placeholder = "Transport Source Port";
+            var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
+            var help = "Range: 1 - 65535";
+            var action = 'SET_TP_SRC';
+            var name = "Source Port";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_TP_DST" :
+            var h3 = "Set Transport Destination Port";
+            var placeholder = "Transport Destination Port";
+            var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
+            var help = "Range: 1 - 65535";
+            var action = 'SET_TP_DST';
+            var name = "Destination Port";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "ENQUEUE" :
+            var h3 = "Enqueue";
+            var placeholder = "Enqueue";
+            var id = one.f.flows.id.modal.action.enqueue;
+            var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addEnqueue, one.f.flows.modal.action.add.addEnqueue);
+            $modal.modal();
+            break;
+          case "DROP" :
+            var name = "Drop";
+            var action = 'DROP';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "LOOPBACK" :
+            var name = "Loopback";
+            var action = 'LOOPBACK';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "FLOOD" :
+            var name = "Flood";
+            var action = 'FLOOD';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "FLOOD_ALL" :
+            var name = "Flood All";
+            var action = 'FLOOD_ALL';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "SW_PATH" :
+            var name = "Software Path";
+            var action = 'SW_PATH';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "HW_PATH" :
+            var name = "Hardware Path";
+            var action = 'HW_PATH';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "CONTROLLER" :
+            var name = "Controller";
+            var action = 'CONTROLLER';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+        }
+      },
+      initialize : function(h3, bodyCallback, addCallback) {
+        var footer = one.f.flows.modal.action.footer();
+        var $body = bodyCallback();
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
+        // bind close button
+        $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        // bind add flow button
+        $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
+          addCallback($modal);
+        });
+        return $modal;
+      },
+      add : {
+        addOutputPorts : function($modal) {
+          var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+          var ports = '';
+          var pid = '';
+          $options.each(function(index, value) {
+            ports = ports+$(value).text()+", ";
+            pid = pid+$(value).attr('value')+",";
+          });
+          ports = ports.slice(0,-2);
+          pid = pid.slice(0,-1);
+          one.f.flows.modal.action.add.addPortsToTable(ports, pid);
+          $modal.modal('hide');
         },
-        ajax : {
-            nodes : function(successCallback) {
-                $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
-                    var nodes = one.f.flows.modal.data.nodes(data);
-                    var nodeports = data;
-                    one.f.flows.registry['nodes'] = nodes;
-                    one.f.flows.registry['nodeports'] = nodeports;
-
-                    successCallback(nodes, nodeports);
-                });
-            },
-            saveflow : function(resource, callback) {
-                $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
-                    callback(data);
-                });
-            },
-            removeflow : function(id, node, callback) {
-                resource = {};
-                resource['action'] = 'remove';
-                $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
-                    callback(data);
-                });
-            },
-            toggleflow : function(id, node, callback) {
-                resource = {};
-                resource['action'] = 'toggle';
-                $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
-                    callback(data);
-                });
-            }
+        addEnqueue : function($modal) {
+          var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+          var ports = '';
+          var pid = '';
+          $options.each(function(index, value) {
+            ports = ports+$(value).text()+", ";
+            pid = pid+$(value).attr('value')+",";
+          });
+          var $input = $('#'+one.f.flows.id.modal.action.queue);
+          var queue = $input.val();
+          ports = ports.slice(0,-2);
+          pid = pid.slice(0,-1);
+          one.f.flows.modal.action.add.addEnqueueToTable(ports, pid, queue);
+          $modal.modal('hide');
         },
-        data : {
-            nodes : function(data) {
-                result = {};
-                $.each(data, function(key, value) {
-                    result[key] = value['name'];
-                });
-                return result;
-            }
+        addEnqueueToTable : function(ports, pid, queue) {
+          if (queue !== '' && queue >= 0) {
+            ports += ':'+queue;
+          }
+          var $tr = one.f.flows.modal.action.table.add("Enqueue", ports);
+          $tr.attr('id', 'ENQUEUE');
+          if (queue !== '' && queue >= 0) {
+            $tr.data('action', 'ENQUEUE='+pid+':'+queue);
+          } else {
+            $tr.data('action', 'ENQUEUE='+pid+':0'); // default queue to 0
+          }
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
         },
-        body : function(nodes, nodeports, edit) {
-            var $form = $(document.createElement('form'));
-            var $fieldset = $(document.createElement('fieldset'));
-            var existingFlow;
-            // flow description
-            var $legend = one.lib.form.legend("");
-            $legend.css('visibility', 'hidden');
-            $fieldset.append($legend);
-            // name
-            var $label = one.lib.form.label("Name");
-            var $input = one.lib.form.input("Flow Name");
-            $input.attr('id', one.f.flows.id.modal.form.name);
-            if(edit) {
-                $input.attr('disabled', 'disabled');
-                var flows = one.f.flows.registry.flows;
-                $(flows).each(function(index, value) {
-                  if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
-                    existingFlow = value.flow;
-                  }
-                });
-                $input.val(existingFlow.name);
-            }
-
-            $fieldset.append($label).append($input);
-            // 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.flows.id.modal.form.nodes);
-            if(edit) {
-                $select.attr('disabled', 'disabled');
-                $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
-            }
+        addPortsToTable : function(ports, pid){
+          var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
+          $tr.attr('id', 'OUTPUT');
+          $tr.data('action', 'OUTPUT='+pid);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        add : function(name, action) {
+          var $tr = one.f.flows.modal.action.table.add(name);
+          $tr.attr('id', action);
+          $tr.data('action', action);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        set : function(name, id, action, $modal) {
+          var $input = $('#'+id);
+          var value = $input.val();
+          one.f.flows.modal.action.add.addDataToTable(name,value,action)
+            $modal.modal('hide');
+        },
+        addDataToTable : function(name,value,action) {
+          var $tr = one.f.flows.modal.action.table.add(name, value);
+          $tr.attr('id', action);
+          $tr.data('action', action+'='+value);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        remove : function(that) {
+          $(that).remove();
+          var $table = $('#'+one.f.flows.id.modal.action.table);
+          if ($table.find('tbody').find('tr').size() == 0) {
+            var $tr = $(document.createElement('tr'));
+            var $td = $(document.createElement('td'));
+            $td.attr('colspan', '3');
+            $tr.addClass('empty');
+            $td.text('No data available');
+            $tr.append($td);
+            $table.find('tbody').append($tr);
+          }
+        },
+        modal : {
+          initialize : function(that) {
+            var h3 = "Remove Action";
+            var footer = one.f.flows.modal.action.add.modal.footer();
+            var $body = one.f.flows.modal.action.add.modal.body();
+            var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
+
+            // bind cancel button
+            $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
+              $modal.modal('hide');
+            });
 
-            // bind onchange
-            $select.change(function() {
-                // retrieve port value
-                var node = $(this).find('option:selected').attr('value');
-                var $ports = $('#'+one.f.flows.id.modal.form.port);
-                if (node == '') {
-                    one.lib.form.select.inject($ports, {});
-                    return;
-                }
-                one.f.flows.registry['currentNode'] = node;
-                var ports = nodeports[node]['ports'];
-                one.lib.form.select.inject($ports, ports);
-                one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
-                $ports.val($ports.find("option:first").val());
-                if(edit) {
-                    $ports.val( existingFlow.ingressPort );
-                }
+            // bind remove button
+            $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
+              one.f.flows.modal.action.add.remove(that);
+              $modal.modal('hide');
             });
 
-            $fieldset.append($label).append($select);
-            // input port
-            var $label = one.lib.form.label("Input Port");
-            var $select = one.lib.form.select.create();
-
-            $select.attr('id', one.f.flows.id.modal.form.port);
-            $fieldset.append($label).append($select);
-            // priority
-            var $label = one.lib.form.label("Priority");
-            var $input = one.lib.form.input("Priority");
-            $input.attr('id', one.f.flows.id.modal.form.priority);
-            $input.val('500');
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.priority);
-            }
-            // hardTimeout
-            var $label = one.lib.form.label("Hard Timeout");
-            var $input = one.lib.form.input("Hard Timeout");
-            $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
-            if(edit) {
-                $input.val(existingFlow.hardTimeout);
-            }
-            $fieldset.append($label).append($input);
-
-            // idleTimeout
-            var $label = one.lib.form.label("Idle Timeout");
-            var $input = one.lib.form.input("Idle Timeout");
-            $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.idleTimeout);
-            }
-            // cookie
-            var $label = one.lib.form.label("Cookie");
-            var $input = one.lib.form.input("Cookie");
-            $input.attr('id', one.f.flows.id.modal.form.cookie);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.cookie);
-            }
+            $modal.modal();
+          },
+          body : function() {
+            var $p = $(document.createElement('p'));
+            $p.append("Remove this action?");
+            return $p;
+          },
+          footer : function() {
+            var footer = [];
 
-            // layer 2
-            var $legend = one.lib.form.legend("Layer 2");
-            $fieldset.append($legend);
-            // etherType
-            var $label = one.lib.form.label("Ethernet Type");
-            var $input = one.lib.form.input("Ethernet Type");
-            $input.attr('id', one.f.flows.id.modal.form.etherType);
-            $input.val('0x800');
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.etherType);
-            }
-            // vlanId
-            var $label = one.lib.form.label("VLAN Identification Number");
-            var $input = one.lib.form.input("VLAN Identification Number");
-            $input.attr('id', one.f.flows.id.modal.form.vlanId);
-            var $help = one.lib.form.help("Range: 0 - 4095");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.vlanId);
-            }
+            var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
+            var $removeButton = one.lib.dashlet.button.button(removeButton);
+            footer.push($removeButton);
 
-            // vlanPriority
-            var $label = one.lib.form.label("VLAN Priority");
-            var $input = one.lib.form.input("VLAN Priority");
-            $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
-            var $help = one.lib.form.help("Range: 0 - 7");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.vlanPriority);
-            }
+            var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
+            var $cancelButton = one.lib.dashlet.button.button(cancelButton);
+            footer.push($cancelButton);
 
-            // srcMac
-            var $label = one.lib.form.label("Source MAC Address");
-            var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
-            $input.attr('id', one.f.flows.id.modal.form.srcMac);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.srcMac);
-            }
-            // dstMac
-            var $label = one.lib.form.label("Destination MAC Address");
-            var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
-            $input.attr('id', one.f.flows.id.modal.form.dstMac);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.dstMac);
-            }
-            // layer 3
-            var $legend = one.lib.form.legend("Layer 3");
-            $fieldset.append($legend);
-
-            // srcIp
-            var $label = one.lib.form.label("Source IP Address");
-            var $input = one.lib.form.input("192.168.3.128");
-            $input.attr('id', one.f.flows.id.modal.form.srcIp);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.srcIp);
-            }
-            // dstIp
-            var $label = one.lib.form.label("Destination IP Address");
-            var $input = one.lib.form.input("2001:2334::0/32");
-            $input.attr('id', one.f.flows.id.modal.form.dstIp);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.dstIp);
-            }
-            // tosBits
-            var $label = one.lib.form.label("ToS Bits");
-            var $input = one.lib.form.input("ToS Bits");
-            $input.attr('id', one.f.flows.id.modal.form.tosBits);
-            var $help = one.lib.form.help("Range: 0 - 63");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.tosBits);
-            }
+            return footer;
+          }
+        }
+      },
+      table : {
+        add : function(action, data) {
+          var $tr = $(document.createElement('tr'));
+          var $td = $(document.createElement('td'));
+          $td.append(action);
+          $tr.append($td);
+          var $td = $(document.createElement('td'));
+          if (data != undefined) $td.append(data);
+          $tr.append($td);
+          return $tr;
+        },
+        append : function($tr) {
+          var $table = $('#'+one.f.flows.id.modal.action.table);
+          var $empty = $table.find('.empty').parent();
+          if ($empty.size() > 0) $empty.remove();
+          $table.append($tr);
+        }
+      },
+      body : {
+        common : function() {
+          var $form = $(document.createElement('form'));
+          var $fieldset = $(document.createElement('fieldset'));
+          return [$form, $fieldset];
+        },
+        addOutputPorts : function() {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // output port
+          $label = one.lib.form.label("Select Output Ports");
+          if (one.f.flows.registry.currentNode == undefined){
+            return; //Selecting Output ports without selecting node throws an exception
+          }
+          var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+          $select = one.lib.form.select.create(ports, true);
+          $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+          $fieldset.append($label).append($select);
+          $form.append($fieldset);
+          return $form;
+        },
+        addEnqueue : function() {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // output port
+          $label = one.lib.form.label("Select Output Ports");
+          if (one.f.flows.registry.currentNode == undefined){
+            return; //Selecting Output ports without selecting node throws an exception
+          }
+          var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+          $select = one.lib.form.select.create(ports);
+          $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+          $fieldset.append($label).append($select);
+          $label = one.lib.form.label('Queue (Optional)');
+          $input = one.lib.form.input('Queue')
+          .attr('id', one.f.flows.id.modal.action.queue);
+          $help = one.lib.form.help('Range: 1 - 2147483647');
+          $fieldset.append($label).append($input).append($help);
+          $form.append($fieldset);
+          return $form;
+        },
+        set : function(label, placeholder, id, help) {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // input
+          $label = one.lib.form.label(label);
+          $input = one.lib.form.input(placeholder);
+          $input.attr('id', id);
+          $help = one.lib.form.help(help);
+          // append
+          $fieldset.append($label).append($input).append($help);
+          $form.append($fieldset);
+          return $form;
+        }
+      },
+      footer : function() {
+        var footer = [];
+        var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
+        var $addButton = one.lib.dashlet.button.button(addButton);
+        footer.push($addButton);
+
+        var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
+        var $closeButton = one.lib.dashlet.button.button(closeButton);
+        footer.push($closeButton);
+
+        return footer;
+      }
+    },
+    footer : function() {
+      var footer = [];
 
-            // layer 4
-            var $legend = one.lib.form.legend("Layer 4");
-            $fieldset.append($legend);
-            // srcPort
-            var $label = one.lib.form.label("Source Port");
-            var $input = one.lib.form.input("Source Port");
-            $input.attr('id', one.f.flows.id.modal.form.srcPort);
-            var $help = one.lib.form.help("Range: 0 - 65535");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.srcPort);
-            }
-            // dstPort
-            var $label = one.lib.form.label("Destination Port");
-            var $input = one.lib.form.input("Destination Port");
-            $input.attr('id', one.f.flows.id.modal.form.dstPort);
-            var $help = one.lib.form.help("Range: 0 - 65535");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.dstPort);
-            }
-            // protocol
-            var $label = one.lib.form.label("Protocol");
-            var $input = one.lib.form.input("Protocol");
-            $input.attr('id', one.f.flows.id.modal.form.protocol);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.protocol);
-            }
-            // actions
-            var $legend = one.lib.form.label("Actions");
-            $fieldset.append($legend);
-            // actions table
-            var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
-            var $table = one.lib.dashlet.table.table(tableAttributes);
-            $table.attr('id', one.f.flows.id.modal.action.table);
-            var tableHeaders = ["Action", "Data"];
-            var $thead = one.lib.dashlet.table.header(tableHeaders);
-            var $tbody = one.lib.dashlet.table.body("", tableHeaders);
-            $table.append($thead).append($tbody);
-            // actions
-            var actions = {
-                "" : "Please Select an Action",
-                "DROP" : "Drop",
-                "LOOPBACK" : "Loopback",
-                "FLOOD" : "Flood",
-                "SW_PATH" : "Software Path",
-                "HW_PATH" : "Hardware Path",
-                "CONTROLLER" : "Controller",
-                "OUTPUT" : "Add Output Ports",
-                "SET_VLAN_ID" : "Set VLAN ID",
-                "SET_VLAN_PCP" : "Set VLAN Priority",
-                "POP_VLAN" : "Strip VLAN Header",
-                "SET_DL_SRC" : "Modify Datalayer Source Address",
-                "SET_DL_DST" : "Modify Datalayer Destination Address",
-                "SET_NW_SRC" : "Modify Network Source Address",
-                "SET_NW_DST" :"Modify Network Destination Address",
-                "SET_NW_TOS" : "Modify ToS Bits",
-                "SET_TP_SRC" : "Modify Transport Source Port",
-                "SET_TP_DST" : "Modify Transport Destination Port"
-            };
-            var $select = one.lib.form.select.create(actions);
-            // when selecting an action
-            $select.change(function() {
-                var action = $(this).find('option:selected');
-                one.f.flows.modal.action.parse(action.attr('value'));
-                $select[0].selectedIndex = 0;
-            });
+      var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
+      var $installButton = one.lib.dashlet.button.button(installButton);
+      footer.push($installButton);
 
-            if(edit) {
-                $(existingFlow.actions).each(function(index, value){
-                    setTimeout(function(){
-                        var locEqualTo = value.indexOf("=");
-                        if ( locEqualTo == -1 ) {
-                            one.f.flows.modal.action.add.add(actions[value], value);
-                        } else {
-                            var action = value.substr(0,locEqualTo);
-                            if( action == "OUTPUT") {
-                                var portIds = value.substr(locEqualTo+1).split(",");
-                                var ports = [];
-                                var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
-                                for(var i =0; i < portIds.length ; i++) {
-                                    var portName = allPorts[portIds[i]];
-                                    ports.push(portName);
-                                }
-                                one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
-                            } else {
-                                var val = value.substr(locEqualTo+1);
-                                one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
-                            }
-                        }
-                    }, 1000)
-                });
-            }
-            $fieldset.append($select).append($table);
+      var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
+      var $addButton = one.lib.dashlet.button.button(addButton);
+      footer.push($addButton);
 
-            // return
-            $form.append($fieldset);
-            return $form;
-        },
-        action : {
-            parse : function(option) {
-                switch (option) {
-                    case "OUTPUT" :
-                        var h3 = "Add Output Port";
-                        var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
-                        $modal.modal();
-                        break;
-                    case "SET_VLAN_ID" :
-                        var h3 = "Set VLAN ID";
-                        var placeholder = "VLAN Identification Number";
-                        var id = one.f.flows.id.modal.action.setVlanId;
-                        var help = "Range: 0 - 4095";
-                        var action = 'SET_VLAN_ID';
-                        var name = "VLAN ID";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_VLAN_PCP" :
-                        var h3 = "Set VLAN Priority";
-                        var placeholder = "VLAN Priority";
-                        var id = one.f.flows.id.modal.action.setVlanPriority;
-                        var help = "Range: 0 - 7";
-                        var action = 'SET_VLAN_PCP';
-                        var name = "VLAN Priority";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "POP_VLAN" :
-                        var name = "Strip VLAN Header";
-                        var action = 'POP_VLAN';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "SET_DL_SRC" :
-                        var h3 = "Set Source MAC Address";
-                        var placeholder = "Source MAC Address";
-                        var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
-                        var help = "Example: 00:11:22:aa:bb:cc";
-                        var action = 'SET_DL_SRC';
-                        var name = "Source MAC";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_DL_DST" :
-                        var h3 = "Set Destination MAC Address";
-                        var placeholder = "Destination MAC Address";
-                        var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
-                        var help = "Example: 00:11:22:aa:bb:cc";
-                        var action = 'SET_DL_DST';
-                        var name = "Destination MAC";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_SRC" :
-                        var h3 = "Set IP Source Address";
-                        var placeholder = "Source IP Address";
-                        var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
-                        var help = "Example: 127.0.0.1";
-                        var action = 'SET_NW_SRC';
-                        var name = "Source IP";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_DST" :
-                        var h3 = "Set IP Destination Address";
-                        var placeholder = "Destination IP Address";
-                        var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
-                        var help = "Example: 127.0.0.1";
-                        var action = 'SET_NW_DST';
-                        var name = "Destination IP";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_TOS" :
-                        var h3 = "Set IPv4 ToS";
-                        var placeholder = "IPv4 ToS";
-                        var id = one.f.flows.id.modal.action.modifyTosBits;
-                        var help = "Range: 0 - 63";
-                        var action = 'SET_NW_TOS';
-                        var name = "ToS Bits";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_TP_SRC" :
-                        var h3 = "Set Transport Source Port";
-                        var placeholder = "Transport Source Port";
-                        var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
-                        var help = "Range: 1 - 65535";
-                        var action = 'SET_TP_SRC';
-                        var name = "Source Port";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_TP_DST" :
-                        var h3 = "Set Transport Destination Port";
-                        var placeholder = "Transport Destination Port";
-                        var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
-                        var help = "Range: 1 - 65535";
-                        var action = 'SET_TP_DST';
-                        var name = "Destination Port";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "DROP" :
-                        var name = "Drop";
-                        var action = 'DROP';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "LOOPBACK" :
-                        var name = "Loopback";
-                        var action = 'LOOPBACK';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "FLOOD" :
-                        var name = "Flood";
-                        var action = 'FLOOD';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "SW_PATH" :
-                        var name = "Software Path";
-                        var action = 'SW_PATH';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "HW_PATH" :
-                        var name = "Hardware Path";
-                        var action = 'HW_PATH';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "CONTROLLER" :
-                        var name = "Controller";
-                        var action = 'CONTROLLER';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                }
-            },
-            initialize : function(h3, bodyCallback, addCallback) {
-                var footer = one.f.flows.modal.action.footer();
-                var $body = bodyCallback();
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
-                // bind close button
-                $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-                // bind add flow button
-                $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
-                    addCallback($modal);
-                });
-                return $modal;
-            },
-            add : {
-                addOutputPorts : function($modal) {
-                    var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
-                    var ports = '';
-                    var pid = '';
-                    $options.each(function(index, value) {
-                        ports = ports+$(value).text()+", ";
-                        pid = pid+$(value).attr('value')+",";
-                    });
-                    ports = ports.slice(0,-2);
-                    pid = pid.slice(0,-1);
-                    one.f.flows.modal.action.add.addPortsToTable(ports, pid);
-                    $modal.modal('hide');
-                },
-                addPortsToTable : function(ports, pid){
-                    var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
-                    $tr.attr('id', 'OUTPUT');
-                    $tr.data('action', 'OUTPUT='+pid);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                add : function(name, action) {
-                    var $tr = one.f.flows.modal.action.table.add(name);
-                    $tr.attr('id', action);
-                    $tr.data('action', action);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                set : function(name, id, action, $modal) {
-                    var $input = $('#'+id);
-                    var value = $input.val();
-                    one.f.flows.modal.action.add.addDataToTable(name,value,action)
-                    $modal.modal('hide');
-                },
-                addDataToTable : function(name,value,action) {
-                    var $tr = one.f.flows.modal.action.table.add(name, value);
-                    $tr.attr('id', action);
-                    $tr.data('action', action+'='+value);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                remove : function(that) {
-                    $(that).remove();
-                    var $table = $('#'+one.f.flows.id.modal.action.table);
-                    if ($table.find('tbody').find('tr').size() == 0) {
-                        var $tr = $(document.createElement('tr'));
-                        var $td = $(document.createElement('td'));
-                        $td.attr('colspan', '3');
-                        $tr.addClass('empty');
-                        $td.text('No data available');
-                        $tr.append($td);
-                        $table.find('tbody').append($tr);
-                    }
-                },
-                modal : {
-                    initialize : function(that) {
-                        var h3 = "Remove Action";
-                        var footer = one.f.flows.modal.action.add.modal.footer();
-                        var $body = one.f.flows.modal.action.add.modal.body();
-                        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
-
-                        // bind cancel button
-                        $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
-                            $modal.modal('hide');
-                        });
-
-                        // bind remove button
-                        $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
-                            one.f.flows.modal.action.add.remove(that);
-                            $modal.modal('hide');
-                        });
-
-                        $modal.modal();
-                    },
-                    body : function() {
-                        var $p = $(document.createElement('p'));
-                        $p.append("Remove this action?");
-                        return $p;
-                    },
-                    footer : function() {
-                        var footer = [];
-
-                        var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
-                        var $removeButton = one.lib.dashlet.button.button(removeButton);
-                        footer.push($removeButton);
-
-                        var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
-                        var $cancelButton = one.lib.dashlet.button.button(cancelButton);
-                        footer.push($cancelButton);
-
-                        return footer;
-                    }
-                }
-            },
-            table : {
-                add : function(action, data) {
-                    var $tr = $(document.createElement('tr'));
-                    var $td = $(document.createElement('td'));
-                    $td.append(action);
-                    $tr.append($td);
-                    var $td = $(document.createElement('td'));
-                    if (data != undefined) $td.append(data);
-                    $tr.append($td);
-                    return $tr;
-                },
-                append : function($tr) {
-                    var $table = $('#'+one.f.flows.id.modal.action.table);
-                    var $empty = $table.find('.empty').parent();
-                    if ($empty.size() > 0) $empty.remove();
-                    $table.append($tr);
-                }
-            },
-            body : {
-                common : function() {
-                    var $form = $(document.createElement('form'));
-                    var $fieldset = $(document.createElement('fieldset'));
-                    return [$form, $fieldset];
-                },
-                addOutputPorts : function() {
-                    var common = one.f.flows.modal.action.body.common();
-                    var $form = common[0];
-                    var $fieldset = common[1];
-                    // output port
-                    $label = one.lib.form.label("Select Output Ports");
-                    if (one.f.flows.registry.currentNode == undefined){
-                        return; //Selecting Output ports without selecting node throws an exception
-                    }
-                    var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
-                    $select = one.lib.form.select.create(ports, true);
-                    $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
-                    $fieldset.append($label).append($select);
-                    $form.append($fieldset);
-                    return $form;
-                },
-                set : function(label, placeholder, id, help) {
-                    var common = one.f.flows.modal.action.body.common();
-                    var $form = common[0];
-                    var $fieldset = common[1];
-                    // input
-                    $label = one.lib.form.label(label);
-                    $input = one.lib.form.input(placeholder);
-                    $input.attr('id', id);
-                    $help = one.lib.form.help(help);
-                    // append
-                    $fieldset.append($label).append($input).append($help);
-                    $form.append($fieldset);
-                    return $form;
-                }
-            },
-            footer : function() {
-                var footer = [];
-                var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
-                var $addButton = one.lib.dashlet.button.button(addButton);
-                footer.push($addButton);
-
-                var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
-                var $closeButton = one.lib.dashlet.button.button(closeButton);
-                footer.push($closeButton);
-
-                return footer;
-            }
-        },
-        footer : function() {
-            var footer = [];
+      var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
 
-            var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
-            var $installButton = one.lib.dashlet.button.button(installButton);
-            footer.push($installButton);
+      return footer;
+    },
+    footerEdit : function() {
+      var footer = [];
 
-            var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
-            var $addButton = one.lib.dashlet.button.button(addButton);
-            footer.push($addButton);
+      var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
+      var $editButton = one.lib.dashlet.button.button(editButton);
+      footer.push($editButton);
 
-            var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
+      var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
 
-            return footer;
-        },
-        footerEdit : function() {
-            var footer = [];
+      return footer;
+    },
+    removeMultiple: {
+      dialog: function(flows) {
+        var h3 = 'Remove Flow Entry';
+        var flowList = [];
+        for (var i = 0; i < flows.length; i++) {
+          flowList.push(flows[i]["name"]);
+        }
+        var footer = one.f.flows.modal.removeMultiple.footer();
+        var $body = one.f.flows.modal.removeMultiple.body(flowList);
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
 
-            var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
-            var $editButton = one.lib.dashlet.button.button(editButton);
-            footer.push($editButton);
+        // bind close button
+        $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
 
-            var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
+        // bind remove rule button
+        $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
+          var resource = {};
+          resource['body'] = JSON.stringify(flows);
 
-            return footer;
-        },
-        removeMultiple: {
-            dialog: function(flows) {
-                var h3 = 'Remove Flow Entry';
-                var flowList = [];
-                for (var i = 0; i < flows.length; i++) {
-                    flowList.push(flows[i]["name"]);
-                }
-                var footer = one.f.flows.modal.removeMultiple.footer();
-                var $body = one.f.flows.modal.removeMultiple.body(flowList);
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
-
-                // bind close button
-                $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-
-                // bind remove rule button
-                $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
-                    var resource = {};
-                    resource['body'] = JSON.stringify(flows);
-
-                    $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
-                        $modal.modal('hide');
-                        if(response == "Success") {
-                            one.lib.alert("Flow Entry(s) successfully removed");
-                        } else {
-                            one.lib.alert(response);
-                        }
-                        one.main.dashlet.right.bottom.empty();
-                        one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    });
-                });
-                $modal.modal();
-            },
-            footer : function() {
-                var footer = [];
-                var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
-                var $remove = one.lib.dashlet.button.button(remove);
-                footer.push($remove);
-
-                var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
-                var $cancel = one.lib.dashlet.button.button(cancel);
-                footer.push($cancel);
-
-                return footer;
-            },
-            body : function (flows) {
-                var $p = $(document.createElement('p'));
-                var p = 'Remove the following Flow Entry(s)?';
-                //creata a BS label for each rule and append to list
-                $(flows).each(function(){
-                    var $span = $(document.createElement('span'));
-                    $span.append(this);
-                    p += '<br/>' + $span[0].outerHTML;
-                });
-                $p.append(p);
-                return $p;
+          $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+            $modal.modal('hide');
+            if(response == "Success") {
+              one.lib.alert("Flow Entry(s) successfully removed");
+            } else {
+              one.lib.alert(response);
             }
-        }
-    },
-    ajax : {
-        dashlet : function(callback) {
-            $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
-                one.f.flows.registry['flows'] = data.flows;
-                one.f.flows.registry['privilege'] = data.privilege;
-                one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+            one.main.dashlet.right.bottom.empty();
+            one.f.detail.dashlet(one.main.dashlet.right.bottom);
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          });
+        });
+        $modal.modal();
+      },
+      footer : function() {
+        var footer = [];
+        var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
+        var $remove = one.lib.dashlet.button.button(remove);
+        footer.push($remove);
+
+        var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
+        var $cancel = one.lib.dashlet.button.button(cancel);
+        footer.push($cancel);
+
+        return footer;
+      },
+      body : function (flows) {
+        var $p = $(document.createElement('p'));
+        var p = 'Remove the following Flow Entry(s)?';
+        //creata a BS label for each rule and append to list
+        $(flows).each(function(){
+          var $span = $(document.createElement('span'));
+          $span.append(this);
+          p += '<br/>' + $span[0].outerHTML;
+        });
+        $p.append(p);
+        return $p;
+      }
+    }
+  },
+  ajax : {
+    dashlet : function(callback) {
+      $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
+        one.f.flows.registry['flows'] = data.flows;
+        one.f.flows.registry['privilege'] = data.privilege;
+        one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+
+        callback(data);
+      });
+    }
+  },
+  data : {
+    flowsDataGrid: function(data) {
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'selector',
+      label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
+      sortable: false
+      },
+      {
+        property: 'name',
+      label: 'Flow Name',
+      sortable: true
+      },
+      {
+        property: 'node',
+      label: 'Node',
+      sortable: true
+      }
+      ],
+      data: data.flows,
+      formatter: function(items) {
+        $.each(items, function(index, item) {
+          var $checkbox = document.createElement("input");
+          $checkbox.setAttribute("type", "checkbox");
+          $checkbox.setAttribute("name", item.name);
+          $checkbox.setAttribute("node", item.nodeId);
+          $checkbox.setAttribute('class','flowEntry')
+          item.selector = $checkbox.outerHTML;
+        item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
+          ' data-flowstatus=' + item["flow"]["status"] + 
+          ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
+        });
 
-                callback(data);
-            });
-        }
-    },
-    data : {
-        flowsDataGrid: function(data) {
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'selector',
-                            label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
-                            sortable: false
-                        },
-                        {
-                            property: 'name',
-                            label: 'Flow Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'node',
-                            label: 'Node',
-                            sortable: true
-                        }
-                    ],
-                    data: data.flows,
-                    formatter: function(items) {
-                        $.each(items, function(index, item) {
-                            var $checkbox = document.createElement("input");
-                            $checkbox.setAttribute("type", "checkbox");
-                            $checkbox.setAttribute("name", item.name);
-                            $checkbox.setAttribute("node", item.nodeId);
-                            $checkbox.setAttribute('class','flowEntry')
-                            item.selector = $checkbox.outerHTML;
-                                  item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
-                                ' data-flowstatus=' + item["flow"]["status"] + 
-                                ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
-                        });
-
-                    },
-                    delay: 0
-                });
-            return source;
-        },
-        dashlet : function(data) {
-            var body = [];
-            $(data).each(function(index, value) {
-                var tr = {};
-                var entry = [];
-
-                
-                entry.push(value['name']);
-                entry.push(value['node']);
-                if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
-                    tr['type'] = ['success'];
-                else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
-                    tr['type'] = ['warning'];
-                else 
-                    tr['type'] = ['warning'];
-                tr['entry'] = entry;
-                tr['id'] = value['nodeId'];
-
-                body.push(tr);
-            });
-            return body;
-        }
+      },
+      delay: 0
+      });
+      return source;
     },
-    body : {
-        dashlet : function(body, callback) {
-            var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
-            var $table = one.lib.dashlet.table.table(attributes);
-
-            var headers = ['Flow Name', 'Node'];
-                
-            var $thead = one.lib.dashlet.table.header(headers);
-            $table.append($thead);
-
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
-            return $table;
-        }
+    dashlet : function(data) {
+      var body = [];
+      $(data).each(function(index, value) {
+        var tr = {};
+        var entry = [];
+
+
+        entry.push(value['name']);
+        entry.push(value['node']);
+        if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
+        tr['type'] = ['success'];
+        else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
+        tr['type'] = ['warning'];
+        else 
+        tr['type'] = ['warning'];
+      tr['entry'] = entry;
+      tr['id'] = value['nodeId'];
+
+      body.push(tr);
+      });
+      return body;
     }
+  },
+  body : {
+    dashlet : function(body, callback) {
+      var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
+      var $table = one.lib.dashlet.table.table(attributes);
+
+      var headers = ['Flow Name', 'Node'];
+
+      var $thead = one.lib.dashlet.table.header(headers);
+      $table.append($thead);
+
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
+      return $table;
+    }
+  }
 }
 
 /** 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.populate = function($dashlet, header) {
-    var $h4 = one.lib.dashlet.header(header);
-    $dashlet.append($h4);
+  var $h4 = one.lib.dashlet.header(header);
+  $dashlet.append($h4);
 };
 
 // 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);
-    // callback based on menu
-    var id = $(this).attr('id');
-    var menu = one.f.dashlet;
-    switch (id) {
-        case menu.flows.id:
-            one.f.flows.dashlet($dashlet);
-            break;
-        case menu.nodes.id:
-            one.f.nodes.dashlet($dashlet);
-            break;
-        case menu.detail.id:
-            one.f.detail.dashlet($dashlet);
-            break;
-    };
+  // 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.flows.id:
+ &n