Add Flood and FloodAll supported actions
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / FlowProgrammerService.java
index 96ea6cd14f9612aa80a328faab5b1160277ec0e7..e2fa8d5f1e4f89e0980da755c340d4585a78c379 100644 (file)
@@ -23,7 +23,9 @@ import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExtern
 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
 import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerAware;
 import org.opendaylight.controller.sal.core.IContainerListener;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.Node.NodeIDType;
@@ -34,6 +36,7 @@ import org.opendaylight.controller.sal.flowprogrammer.Flow;
 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.EtherTypes;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.NodeCreator;
@@ -57,7 +60,7 @@ import org.slf4j.LoggerFactory;
  */
 public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         IMessageListener, IContainerListener, IInventoryShimExternalListener,
-        CommandProvider {
+        CommandProvider, IContainerAware {
     private static final Logger log = LoggerFactory
             .getLogger(FlowProgrammerService.class);
     private IController controller;
@@ -65,6 +68,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     private Map<String, Set<NodeConnector>> containerToNc;
     private ConcurrentMap<Long, Map<Integer, Long>> xid2rid;
     private int barrierMessagePriorCount = getBarrierMessagePriorCount();
+    private IPluginOutConnectionService connectionOutService;
 
     public FlowProgrammerService() {
         controller = null;
@@ -83,6 +87,16 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         }
     }
 
+    void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+        connectionOutService = s;
+    }
+
+    void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+        if (connectionOutService == s) {
+            connectionOutService = null;
+        }
+    }
+
     public void setFlowProgrammerNotifier(Map<String, ?> props,
             IFlowProgrammerNotifier s) {
         if (props == null || props.get("containerName") == null) {
@@ -110,7 +124,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     /**
      * Function called by the dependency manager when all the required
      * dependencies are satisfied
-     * 
+     *
      */
     void init() {
         this.controller.addMessageListener(OFType.FLOW_REMOVED, this);
@@ -122,7 +136,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
      * Function called by the dependency manager when at least one dependency
      * become unsatisfied or when the component is shutting down because for
      * example bundle is being stopped.
-     * 
+     *
      */
     void destroy() {
     }
@@ -130,7 +144,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     /**
      * Function called by dependency manager after "init ()" is called and after
      * the services provided by the class are registered in the service registry
-     * 
+     *
      */
     void start() {
     }
@@ -139,39 +153,69 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
      * Function called by the dependency manager before the services exported by
      * the component are unregistered, this will be followed by a "destroy ()"
      * calls
-     * 
+     *
      */
     void stop() {
     }
 
     @Override
     public Status addFlow(Node node, Flow flow) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Add flow will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return addFlowInternal(node, flow, 0);
     }
 
     @Override
     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Modify flow will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return modifyFlowInternal(node, oldFlow, newFlow, 0);
     }
 
     @Override
     public Status removeFlow(Node node, Flow flow) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Remove flow will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return removeFlowInternal(node, flow, 0);
     }
 
     @Override
     public Status addFlowAsync(Node node, Flow flow, long rid) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Add flow Async will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return addFlowInternal(node, flow, rid);
     }
 
     @Override
     public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow,
             long rid) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Modify flow async will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return modifyFlowInternal(node, oldFlow, newFlow, rid);
     }
 
     @Override
     public Status removeFlowAsync(Node node, Flow flow, long rid) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Remove flow async will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return removeFlowInternal(node, flow, rid);
     }
 
@@ -182,6 +226,11 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
                     action, "Invalid node type"));
         }
 
+        Status status = validateFlow(flow);
+        if (!status.isSuccess()) {
+            return status;
+        }
+
         if (controller != null) {
             ISwitch sw = controller.getSwitch((Long) node.getID());
             if (sw != null) {
@@ -201,7 +250,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
                      * will be inserted automatically to synchronize the
                      * progression.
                      */
-                    result = asyncMsgSend(node, sw, msg, rid);  
+                    result = asyncMsgSend(node, sw, msg, rid);
                 }
                 return getStatusInternal(result, action, rid);
             } else {
@@ -213,12 +262,52 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
                 "Internal plugin error"));
     }
 
+    /*
+     * Method which runs openflow 1.0 specific validation on the requested flow
+     * This validation is needed because the openflow switch will silently accept
+     * the request and install only the applicable match fields
+     */
+    private Status validateFlow(Flow flow) {
+        Match m = flow.getMatch();
+        boolean isIPEthertypeSet = m.isPresent(MatchType.DL_TYPE)
+                && (m.getField(MatchType.DL_TYPE).getValue().equals(EtherTypes.IPv4.shortValue()) || m
+                        .getField(MatchType.DL_TYPE).getValue().equals(EtherTypes.IPv6.shortValue()));
+
+        // network address check
+        if ((m.isPresent(MatchType.NW_SRC) || m.isPresent(MatchType.NW_DST)) && !isIPEthertypeSet) {
+            return new Status(StatusCode.NOTACCEPTABLE,
+                    "The match on network source or destination address cannot be accepted if the match "
+                     + "on proper ethertype is missing");
+        }
+
+        // transport protocol check
+        if (m.isPresent(MatchType.NW_PROTO) && !isIPEthertypeSet) {
+            return new Status(StatusCode.NOTACCEPTABLE,
+                    "The match on network protocol cannot be accepted if the match on proper ethertype is missing");
+        }
+
+        // transport ports check
+        if ((m.isPresent(MatchType.TP_SRC) || m.isPresent(MatchType.TP_DST))
+                && (!isIPEthertypeSet || m.isAny(MatchType.NW_PROTO))) {
+            return new Status(
+                    StatusCode.NOTACCEPTABLE,
+                    "The match on transport source or destination port cannot be accepted if the match on network protocol and match on IP ethertype are missing");
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
     private Status modifyFlowInternal(Node node, Flow oldFlow, Flow newFlow, long rid) {
         String action = "modify";
         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
                     action, "Invalid node type"));
         }
+
+        Status status = validateFlow(newFlow);
+        if (!status.isSuccess()) {
+            return status;
+        }
+
         if (controller != null) {
             ISwitch sw = controller.getSwitch((Long) node.getID());
             if (sw != null) {
@@ -324,6 +413,11 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
     @Override
     public Status removeAllFlows(Node node) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Remove all flows will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         return new Status(StatusCode.SUCCESS);
     }
 
@@ -367,7 +461,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
              */
             if (inPort == null
                     || container.equals(GlobalConstants.DEFAULT.toString())
-                    || this.containerToNc.get(container).contains(inPort)) {
+                    || (containerToNc.containsKey(container) && containerToNc.get(container).contains(inPort))) {
                 notifier.flowRemoved(node, flow);
             }
         }
@@ -392,7 +486,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         if ((rid == null) || (rid == 0)) {
             return;
         }
-        
+
         /*
          * Notifies the caller that error has been reported for a previous flow
          * programming request
@@ -400,7 +494,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
                 .entrySet()) {
             IFlowProgrammerNotifier notifier = containerNotifier.getValue();
-            notifier.flowErrorReported(node, rid, errorMsg);
+            notifier.flowErrorReported(node, rid, Utils.getOFErrorString(errorMsg));
         }
     }
 
@@ -418,8 +512,6 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     @Override
     public void nodeConnectorUpdated(String containerName, NodeConnector p,
             UpdateType type) {
-        Set<NodeConnector> target = null;
-
         switch (type) {
         case ADDED:
             if (!containerToNc.containsKey(containerName)) {
@@ -430,7 +522,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         case CHANGED:
             break;
         case REMOVED:
-            target = containerToNc.get(containerName);
+            Set<NodeConnector> target = containerToNc.get(containerName);
             if (target != null) {
                 target.remove(p);
             }
@@ -446,6 +538,11 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
     @Override
     public Status syncSendBarrierMessage(Node node) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("Sync Send Barrier will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
             return new Status(StatusCode.NOTACCEPTABLE,
                     "The node does not support Barrier message.");
@@ -466,9 +563,14 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         return new Status(StatusCode.INTERNALERROR,
                 "Failed to send Barrier message.");
     }
-    
+
     @Override
     public Status asyncSendBarrierMessage(Node node) {
+        if (!connectionOutService.isLocal(node)) {
+            log.debug("ASync Send Barrier will not be processed in a non-master controller for node " + node);
+            return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+        }
+
         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
             return new Status(StatusCode.NOTACCEPTABLE,
                     "The node does not support Barrier message.");
@@ -489,14 +591,14 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         return new Status(StatusCode.INTERNALERROR,
                 "Failed to send Barrier message.");
     }
-    
+
     /**
      * This method sends the message asynchronously until the number of messages
      * sent reaches a threshold. Then a Barrier message is sent automatically
      * for sync purpose. An unique Request ID associated with the message is
      * passed down by the caller. The Request ID will be returned to the caller
      * when an error message is received from the switch.
-     * 
+     *
      * @param node
      *            The node
      * @param msg
@@ -514,25 +616,25 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
         xid = sw.asyncSend(msg);
         addXid2Rid(swid, xid, rid);
-        
+
         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
         if (swxid2rid == null) {
             return result;
         }
-        
+
         int size = swxid2rid.size();
         if (size % barrierMessagePriorCount == 0) {
             result = asyncSendBarrierMessage(node);
         }
-        
+
         return result;
     }
-    
+
     /**
      * A number of async messages are sent followed by a synchronous Barrier
      * message. This method returns the maximum async messages that can be sent
      * before the Barrier message.
-     * 
+     *
      * @return The max count of async messages sent prior to Barrier message
      */
     private int getBarrierMessagePriorCount() {
@@ -548,11 +650,11 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
         return rv;
     }
-    
+
     /**
      * This method returns the message Request ID previously assigned by the
      * caller for a given OF message xid
-     * 
+     *
      * @param swid
      *            The switch id
      * @param xid
@@ -576,14 +678,14 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     /**
      * This method returns a copy of outstanding xid to rid mappings.for a given
      * switch
-     * 
+     *
      * @param swid
      *            The switch id
      * @return a copy of xid2rid mappings
      */
     public Map<Integer, Long> getSwXid2Rid(long swid) {
         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
-        
+
         if (swxid2rid != null) {
             return new HashMap<Integer, Long>(swxid2rid);
         } else {
@@ -593,7 +695,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
     /**
      * Adds xid to rid mapping to the local DB
-     * 
+     *
      * @param swid
      *            The switch id
      * @param xid
@@ -611,7 +713,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     /**
      * When an Error message is received, this method will be invoked to remove
      * the offending xid from the local DB.
-     * 
+     *
      * @param swid
      *            The switch id
      * @param xid
@@ -626,7 +728,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
 
     /**
      * Convert various result into Status
-     * 
+     *
      * @param result
      *            The returned result from previous action
      * @param action
@@ -652,11 +754,11 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
                     "send", action, "Internal Error"));
         }
     }
-    
+
     /**
      * When a Barrier reply is received, this method will be invoked to clear
      * the local DB
-     * 
+     *
      * @param swid
      *            The switch id
      */
@@ -670,7 +772,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
     @Override
     public void updateNode(Node node, UpdateType type, Set<Property> props) {
         long swid = (Long)node.getID();
-        
+
         switch (type) {
         case ADDED:
             Map<Integer, Long> swxid2rid = new HashMap<Integer, Long>();
@@ -712,7 +814,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
             ci.println("Please enter a valid node id");
             return;
         }
-        
+
         long sid;
         try {
             sid = HexEncode.stringToLong(st);
@@ -720,7 +822,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
             ci.println("Please enter a valid node id");
             return;
         }
-        
+
         Map<Integer, Long> swxid2rid = this.xid2rid.get(sid);
         if (swxid2rid == null) {
             ci.println("The node id entered does not exist");
@@ -728,7 +830,7 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         }
 
         ci.println("xid             rid");
-        
+
         Set<Integer> xidSet = swxid2rid.keySet();
         if (xidSet == null) {
             return;
@@ -743,4 +845,14 @@ public class FlowProgrammerService implements IPluginInFlowProgrammerService,
         ci.println("Max num of async messages sent prior to the Barrier message is "
                 + barrierMessagePriorCount);
     }
+
+    @Override
+    public void containerCreate(String containerName) {
+        // do nothing
+    }
+
+    @Override
+    public void containerDestroy(String containerName) {
+        containerToNc.remove(containerName);
+    }
 }