More work toward writing the flow correctly 65/9165/1
authorRob Adams <readams@readams.net>
Fri, 18 Jul 2014 16:49:43 +0000 (09:49 -0700)
committerRob Adams <readams@readams.net>
Fri, 18 Jul 2014 16:49:43 +0000 (09:49 -0700)
* Add beginnings of destination mapper table
* Add some additional configuration information for switches
* Additional tests

Change-Id: I41142b49897e213aec2a00cf53233e0634b0d909
Signed-off-by: Rob Adams <readams@readams.net>
16 files changed:
distribution/pom.xml
groupbasedpolicy/pom.xml
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/endpoint/AbstractEndpointRegistry.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchListener.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchManager.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/DestinationMapper.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/FlowUtils.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/PortSecurity.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/SourceMapper.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyCache.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolver.java
groupbasedpolicy/src/main/yang/renderer/ofoverlay/ofoverlay.yang
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/MockSwitchManager.java [new file with mode: 0644]
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/FlowTableTest.java
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/PortSecurityTest.java

index 4ab5031f6a7d57caa3b391c17d29c90021f53b6e..b60df8717cd8eeb4e862a3b465a970e77e804277 100644 (file)
@@ -33,6 +33,11 @@ see https://git.opendaylight.org/gerrit/#/c/390/
       <artifactId>openflowplugin</artifactId>
       <version>${openflowplugin.distribution.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.openflowplugin</groupId>
+      <artifactId>openflowplugin-api</artifactId>
+      <version>${openflowplugin.distribution.version}</version>
+    </dependency>
 
     <!-- openflowjava -->
     <dependency>
index e1e2ffec7f09ae9fe01953de4366d35a861e3dc6..51abcbce4f07b1661b6ca20e172aeed84a95a109 100644 (file)
       <groupId>org.opendaylight.controller.model</groupId>
       <artifactId>model-flow-service</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.model</groupId>
-      <artifactId>model-flow-statistics</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller.model</groupId>
       <artifactId>model-flow-management</artifactId>
index 8ec98f956554b8b182f0b3d241c5c2eefb45be09..3b12ae769d5e03de23c05148747f40585076a149 100644 (file)
@@ -17,7 +17,6 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
-import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointsBuilder;
@@ -40,6 +39,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -253,8 +253,11 @@ public abstract class AbstractEndpointRegistry
             new Function<RpcResult<TransactionStatus>,RpcResult<Void>>() {
         @Override
         public RpcResult<Void> apply(RpcResult<TransactionStatus> input) {
-            return Rpcs.<Void>getRpcResult(input.isSuccessful(), 
-                                           input.getErrors());
+            if (input.isSuccessful())
+                return RpcResultBuilder.<Void>success().build();
+            else 
+                return RpcResultBuilder.<Void>failed()
+                        .withRpcErrors(input.getErrors()).build();
         }
     };
 }
index 5dbf66b2fa46a2b0dc33fe30e3caaab7865b725d..b7201f764b4100c4cbb360bfe7fc9ea001be0f4d 100644 (file)
@@ -27,6 +27,7 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.DestinationMapper;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowTableCtx;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
@@ -118,8 +119,9 @@ public class PolicyManager
                                             this, policyResolver, switchManager, 
                                             endpointManager, executor);
         flowPipeline = ImmutableList.of(new PortSecurity(ctx),
-                                        new SourceMapper(ctx));
-        
+                                        new SourceMapper(ctx),
+                                        new DestinationMapper(ctx));
+
         policyScope = policyResolver.registerListener(this);
         if (switchManager != null)
             switchManager.registerListener(this);
@@ -181,6 +183,12 @@ public class PolicyManager
         dirty.get().addNode(sw);
         scheduleUpdate();
     }
+    
+    @Override
+    public void switchUpdated(NodeId sw) {
+        dirty.get().addNode(sw);
+        scheduleUpdate();
+    }
 
     // ****************
     // EndpointListener
index 93cb4deffb8f6d7f16ad15e9d7e310c3b9639ba9..a5db1f4b4985689582ab7a2ecf3c82ef46143cd8 100644 (file)
@@ -26,4 +26,11 @@ public interface SwitchListener {
      * @param sw the ID for the switch
      */
     public void switchRemoved(NodeId sw);
+    
+    /**
+     * Indicated that the switch configuration, tunnel port, or external ports
+     * have changed
+     * @param sw
+     */
+    public void switchUpdated(NodeId sw);
 }
index 1f1fed215331b7f933692052a4cfbd0ac218e281..e9c8ced7668e902e5cf1ea99c60d5034e1316821 100644 (file)
@@ -9,7 +9,10 @@
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ScheduledExecutorService;
@@ -19,10 +22,15 @@ import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig.EncapsulationFormat;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -43,8 +51,8 @@ import com.google.common.util.concurrent.ListenableFuture;
  * correctly
  * @author readams
  */
-public class SwitchManager implements AutoCloseable, DataChangeListener {
-    private static final Logger LOG =
+public class SwitchManager implements AutoCloseable {
+    private static final Logger LOG = 
             LoggerFactory.getLogger(SwitchManager.class);
 
     private final DataBroker dataProvider;
@@ -55,19 +63,26 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
             InstanceIdentifier.builder(Nodes.class)
                 .child(Node.class).build();
     private ListenerRegistration<DataChangeListener> nodesReg;
+    private ListenerRegistration<DataChangeListener> nodesConfigReg;
 
-    private ConcurrentHashMap<NodeId, SwitchState> switches =
+    protected ConcurrentHashMap<NodeId, SwitchState> switches = 
             new ConcurrentHashMap<>();
-    private List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
+    protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
 
     public SwitchManager(DataBroker dataProvider,
                          ScheduledExecutorService executor) {
         super();
         this.dataProvider = dataProvider;
-        nodesReg = dataProvider
-                .registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
-                                            nodeIid, this,
-                                            DataChangeScope.ONE);
+        if (dataProvider != null) {
+            nodesReg = dataProvider
+                .registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, 
+                                            nodeIid, new NodesListener(), 
+                                            DataChangeScope.SUBTREE);
+            nodesConfigReg = dataProvider
+                    .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, 
+                                                nodeIid, new NodesConfigListener(), 
+                                                DataChangeScope.SUBTREE);
+        }
         readSwitches();
         LOG.debug("Initialized OFOverlay switch manager");
     }
@@ -94,7 +109,7 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
                                       new Function<SwitchState, NodeId>() {
             @Override
             public NodeId apply(SwitchState input) {
-                return input.switchNode.getId();
+                return input.nodeId;
             }
         });
     }
@@ -109,6 +124,24 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
         if (state == null) return false;
         return SwitchStatus.READY.equals(state.status);
     }
+    
+    public Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
+        SwitchState state = switches.get(nodeId);
+        if (state == null) return Collections.emptySet();
+        return state.externalPorts;
+    }
+    
+    public NodeConnectorId getTunnelPort(NodeId nodeId) {
+        SwitchState state = switches.get(nodeId);
+        if (state == null) return null;
+        return state.tunnelPort;
+    }
+    
+    public IpAddress getTunnelIP(NodeId nodeId) {
+        SwitchState state = switches.get(nodeId);
+        if (state == null || state.nodeConfig == null) return null;
+        return state.nodeConfig.getTunnelIp();
+    }
 
     /**
      * Add a {@link SwitchListener} to get notifications of switch events
@@ -133,29 +166,40 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
     @Override
     public void close() throws Exception {
         nodesReg.close();
+        nodesConfigReg.close();
     }
 
     // ******************
     // DataChangeListener
     // ******************
 
-    @Override
-    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>,
-                                                   DataObject> change) {
-        for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
-            LOG.info("{} removed", iid);
-
-            DataObject old = change.getOriginalData().get(iid);
-            if (old != null && old instanceof Node) {
-                removeSwitch(((Node)old).getId());
+    private class NodesListener implements DataChangeListener {
+
+        @Override
+        public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, 
+                                                       DataObject> change) {
+            for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
+                DataObject old = change.getOriginalData().get(iid);
+                if (old != null && old instanceof Node) {
+                    removeSwitch(((Node)old).getId());
+                }
             }
-        }
 
-        for (DataObject dao : change.getCreatedData().values()) {
-            updateSwitch(dao);
+            for (DataObject dao : change.getCreatedData().values()) {
+                updateSwitch(dao);
+            }
+            for (DataObject dao : change.getUpdatedData().values()) {
+                updateSwitch(dao);
+            }
         }
-        for (DataObject dao : change.getUpdatedData().values()) {
-            updateSwitch(dao);
+    }
+    
+    private class NodesConfigListener implements DataChangeListener {
+
+        @Override
+        public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, 
+                                                       DataObject> change) {
+            readSwitches();
         }
     }
 
@@ -163,6 +207,18 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
     // Implementation
     // **************
 
+    private SwitchState getSwitchState(NodeId id) {
+        SwitchState state = switches.get(id); 
+        if (state == null) {
+            state = new SwitchState(id);
+            SwitchState old = 
+                    switches.putIfAbsent(id, state);
+            if (old != null)
+                state = null;
+        }
+        return state;
+    }
+    
     private void updateSwitch(DataObject dao) {
         if (!(dao instanceof Node)) return;
         // Switches are registered as Nodes in the inventory; OpenFlow switches
@@ -173,14 +229,25 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
 
         LOG.debug("{} update", node.getId());
 
-        SwitchState state = switches.get(node.getId());
-        if (state == null) {
-            state = new SwitchState(node);
-            SwitchState old =
-                    switches.putIfAbsent(node.getId(), state);
-            if (old == null) {
-                switchConnected(node.getId());
-            }
+        SwitchState state = getSwitchState(node.getId());
+
+        state.setNode(node);
+        
+        if (SwitchStatus.DISCONNECTED.equals(state.status))
+            switchConnected(node.getId());
+        else if (SwitchStatus.READY.equals(state.status))
+            notifySwitchUpdated(node.getId());
+    }
+    
+    private void updateSwitchConfig(NodeId nodeId, OfOverlayNodeConfig config) {
+        SwitchState state = getSwitchState(nodeId);
+        state.setConfig(config);
+            notifySwitchUpdated(nodeId);
+    }
+
+    private void notifySwitchUpdated(NodeId nodeId) {
+        for (SwitchListener listener : listeners) {
+            listener.switchUpdated(nodeId);
         }
     }
 
@@ -204,6 +271,27 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
         }
     };
 
+    private final FutureCallback<Optional<Nodes>> readSwitchConfCallback =
+            new FutureCallback<Optional<Nodes>>() {
+        @Override
+        public void onSuccess(Optional<Nodes> result) {
+            if (result.isPresent()) {
+                Nodes nodes = (Nodes)result.get();
+                for (Node node : nodes.getNode()) {
+                    OfOverlayNodeConfig config = 
+                            node.getAugmentation(OfOverlayNodeConfig.class);
+                    if (config != null)
+                        updateSwitchConfig(node.getId(), config);
+                }
+            }
+        }
+
+        @Override
+        public void onFailure(Throwable t) {
+            LOG.error("Count not read switch information", t);
+        }
+    };
+
     /**
      * Read the set of switches from the ODL inventory and update our internal
      * map.
@@ -213,10 +301,16 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
      * notifications
      */
     private void readSwitches() {
-        ListenableFuture<Optional<Nodes>> future =
-                dataProvider.newReadOnlyTransaction()
-                    .read(LogicalDatastoreType.OPERATIONAL,nodesIid);
-        Futures.addCallback(future, readSwitchesCallback);
+        if (dataProvider != null) {
+            ListenableFuture<Optional<Nodes>> future = 
+                    dataProvider.newReadOnlyTransaction()
+                    .read(LogicalDatastoreType.CONFIGURATION, nodesIid);
+            Futures.addCallback(future, readSwitchConfCallback);
+            
+            future = dataProvider.newReadOnlyTransaction()
+                    .read(LogicalDatastoreType.OPERATIONAL, nodesIid);
+            Futures.addCallback(future, readSwitchesCallback);
+        }
     }
 
     /**
@@ -258,7 +352,11 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
         LOG.info("Switch {} removed", nodeId);
     }
 
-    private enum SwitchStatus {
+    protected enum SwitchStatus {
+        /**
+         * The switch is not currently connected
+         */
+        DISCONNECTED,
         /**
          * The switch is connected but not yet configured
          */
@@ -272,14 +370,68 @@ public class SwitchManager implements AutoCloseable, DataChangeListener {
     /**
      * Internal representation of the state of a connected switch
      */
-    private static class SwitchState {
+    protected static class SwitchState {
+        NodeId nodeId;
+
         Node switchNode;
-        SwitchStatus status = SwitchStatus.PREPARING;
-        public SwitchState(Node switchNode) {
+        OfOverlayNodeConfig nodeConfig;
+        
+        NodeConnectorId tunnelPort;
+        Set<NodeConnectorId> externalPorts = Collections.emptySet();
+
+        SwitchStatus status = SwitchStatus.DISCONNECTED;
+        
+        public SwitchState(NodeId switchNode) {
             super();
-            this.switchNode = switchNode;
+            nodeId = switchNode;
         }
 
+        /**
+         * Constructor used for tests
+         */
+        public SwitchState(NodeId node,
+                           NodeConnectorId tunnelPort,
+                           Set<NodeConnectorId> externalPorts) {
+            this.nodeId = node;
+            this.tunnelPort = tunnelPort;
+            this.externalPorts = externalPorts;
+        }
+        
+        private void update() {
+            if (switchNode == null) return;
+            FlowCapableNode fcn = 
+                    switchNode.getAugmentation(FlowCapableNode.class);
+            if (fcn == null) return;
+            
+            List<NodeConnector> ports = switchNode.getNodeConnector();
+            HashSet<NodeConnectorId> externalPorts = new HashSet<>();
+            for (NodeConnector nc : ports) {
+                FlowCapableNodeConnector fcnc = 
+                        nc.getAugmentation(FlowCapableNodeConnector.class);
+                if (fcnc == null || fcnc.getName() == null) continue;
+
+                if (fcnc.getName().matches(".*_(vxlan|tun)\\d+")) {
+                    tunnelPort = nc.getId();
+                }
+                if (nodeConfig != null) {
+                    for (String pattern : nodeConfig.getExternalInterfaces()) {
+                        if (fcnc.getName().matches(pattern))
+                            externalPorts.add(nc.getId());
+                    }
+                }
+            }
+            this.externalPorts = Collections.unmodifiableSet(externalPorts);
+        }
+        
+        public void setNode(Node switchNode) {
+            this.switchNode = switchNode;
+            update();
+        }
+    
+        public void setConfig(OfOverlayNodeConfig config) {
+            nodeConfig = config;
+            update();
+        }
     }
 
 }
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/DestinationMapper.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/DestinationMapper.java
new file mode 100644 (file)
index 0000000..7c818e0
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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.groupbasedpolicy.renderer.ofoverlay.flow;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
+import org.opendaylight.groupbasedpolicy.resolver.EgKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Manage the table that maps the destination address to the next hop
+ * for the path as well as applies any relevant routing transformations.
+ * @author readams
+ */
+public class DestinationMapper extends FlowTable {
+    public static final short TABLE_ID = 2;
+    /**
+     * This is the MAC address of the magical router in the sky
+     */
+    public static final String ROUTER_MAC = "88:f0:31:b5:12:b5";
+
+    public DestinationMapper(FlowTableCtx ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public short getTableId() {
+        return TABLE_ID;
+    }
+
+    @Override
+    public void sync(ReadWriteTransaction t, 
+                     InstanceIdentifier<Table> tiid,
+                     Map<String, FlowCtx> flowMap, 
+                     NodeId nodeId, Dirty dirty)
+                             throws Exception {
+        HashSet<EgKey> visitedEgs = new HashSet<>();
+        for (Endpoint e : ctx.endpointManager.getEndpointsForNode(nodeId)) {
+            if (e.getTenant() == null || e.getEndpointGroup() == null)
+                continue;
+            EgKey key = new EgKey(e.getTenant(), e.getEndpointGroup());
+            syncEPG(t, tiid, flowMap, nodeId, key, visitedEgs);
+            
+            Set<EgKey> peers = ctx.policyResolver
+                    .getProvidersForConsumer(e.getTenant(), 
+                                             e.getEndpointGroup());
+            syncEgKeys(t, tiid, flowMap, nodeId, peers, visitedEgs);
+            peers = ctx.policyResolver
+                    .getConsumersForProvider(e.getTenant(), 
+                                             e.getEndpointGroup());
+            syncEgKeys(t, tiid, flowMap, nodeId, peers, visitedEgs);
+        }
+    }
+    
+    private void syncEgKeys(ReadWriteTransaction t, 
+                            InstanceIdentifier<Table> tiid,
+                            Map<String, FlowCtx> flowMap, 
+                            NodeId nodeId,
+                            Set<EgKey> peers,
+                            HashSet<EgKey> visitedEgs) throws Exception {
+        for (EgKey key : peers) {
+            syncEPG(t, tiid, flowMap, nodeId, key, visitedEgs);
+        }
+    }
+
+    // set up next-hop destinations for all the endpoints in the endpoint
+    // group on the node
+    private void syncEPG(ReadWriteTransaction t, 
+                         InstanceIdentifier<Table> tiid,
+                         Map<String, FlowCtx> flowMap, 
+                         NodeId nodeId,
+                         EgKey key,
+                         HashSet<EgKey> visitedEgs) throws Exception {
+        if (visitedEgs.contains(key)) return;
+        visitedEgs.add(key);
+        
+        Collection<Endpoint> egEps = ctx.endpointManager
+                .getEndpointsForGroup(key.getTenantId(), key.getEgId());
+        for (Endpoint e : egEps) {
+            if (e.getTenant() == null || e.getEndpointGroup() == null)
+                continue;
+            OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
+            if (ofc == null || ofc.getNodeId() == null) continue;
+            
+            syncEPL2(t, tiid, flowMap, nodeId, e, ofc, key);
+        }
+    }
+
+    private void syncEPL2(ReadWriteTransaction t,
+                          InstanceIdentifier<Table> tiid,
+                          Map<String, FlowCtx> flowMap, 
+                          NodeId nodeId, 
+                          Endpoint e, OfOverlayContext ofc,
+                          EgKey key) 
+                                 throws Exception {
+
+        ArrayList<Instruction> instructions = new ArrayList<>();
+        int order = 0;
+        
+        String nextHop;
+        if (LocationType.External.equals(ofc.getLocationType())) {
+            // XXX - TODO - perform NAT and send to the external network
+            nextHop = "external";
+            LOG.warn("External endpoints not yet supported");
+            return;
+        } else {
+            if (Objects.equals(ofc.getNodeId(), nodeId)) {
+                // this is a local endpoint
+                nextHop = ofc.getNodeConnectorId().getValue();
+
+                instructions.add(new InstructionBuilder()
+                    .setOrder(order++)
+                    .setInstruction(FlowUtils.outputActionIns(ofc.getNodeConnectorId()))
+                    .build());
+            } else {
+                // this endpoint is on a different switch; send to the 
+                // appropriate tunnel
+                
+                // XXX - TODO Add action: set tunnel_id from sEPG register
+
+                IpAddress tunDst = 
+                        ctx.switchManager.getTunnelIP(ofc.getNodeId());
+                NodeConnectorId tunPort =
+                        ctx.switchManager.getTunnelPort(nodeId);
+                if (tunDst == null) return;
+                if (tunPort == null) return;
+
+                if (tunDst.getIpv4Address() != null) {
+                    nextHop = tunDst.getIpv4Address().getValue();
+                    
+                    // XXX - TODO Add action: set tunnel dst to tunDst ipv4 
+                } else if (tunDst.getIpv6Address() != null) {
+                    nextHop = tunDst.getIpv6Address().getValue();
+
+                    // XXX - TODO Add action: set tunnel dst to tunDst ipv6 
+                } else {
+                    // this shouldn't happen
+                    LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
+                    return;
+                }
+                
+                instructions.add(new InstructionBuilder()
+                    .setOrder(order++)
+                    .setInstruction(FlowUtils.outputActionIns(tunPort))
+                    .build());
+            }
+        }
+        
+        instructions.add(new InstructionBuilder()
+            .setOrder(order++)
+            .setInstruction(FlowUtils.gotoTable((short)(getTableId()+1)))
+            .build());
+
+        FlowId flowid = new FlowId(new StringBuilder()
+            .append(e.getL2Context())
+            .append("|l2|")
+            .append(e.getMacAddress())
+            .append("|")
+            .append(nextHop)
+            .toString());
+        if (!visit(flowMap, flowid.getValue()))
+            return;
+    
+        FlowBuilder flowb = base()
+            .setId(flowid)
+            .setMatch(new MatchBuilder()
+            .setEthernetMatch(FlowUtils.ethernetMatch(null, 
+                                                      e.getMacAddress(), 
+                                                      null))
+                 .build())
+            .setInstructions(new InstructionsBuilder()
+                .setInstruction(instructions)
+                .build());
+        writeFlow(t, tiid, flowb.build());
+    }
+}
index 5277fb96546d7ac565b50d8ac33aa9ba16b789da..1c93bafba64b60bb66c054a201abe4fb081b80d7 100644 (file)
@@ -9,8 +9,11 @@
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.drop.action._case.DropActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
@@ -20,11 +23,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.ta
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.GoToTableCaseBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCaseBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTableBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.write.actions._case.WriteActionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
@@ -129,35 +134,62 @@ public final class FlowUtils {
         return createFlowPath(table, new FlowKey(flowId));
     }
 
-    public static Instructions gotoTable(short tableId) {
+    public static Instructions gotoTableInstructions(short tableId) {
         return new InstructionsBuilder()
         .setInstruction(ImmutableList.of(new InstructionBuilder()
             .setOrder(Integer.valueOf(0))
-            .setInstruction(new GoToTableCaseBuilder()
-                .setGoToTable(new GoToTableBuilder()
-                    .setTableId(tableId)
-                    .build())
-                .build())
+            .setInstruction(gotoTable(tableId))
             .build()))
         .build();
     }
     
+    public static Instruction gotoTable(short tableId) {
+        return new GoToTableCaseBuilder()
+            .setGoToTable(new GoToTableBuilder()
+                .setTableId(tableId)
+                .build())
+            .build();
+    }
+    
+    public static Instruction outputActionIns(NodeConnectorId id) {
+        return new WriteActionsCaseBuilder()
+            .setWriteActions(new WriteActionsBuilder()
+                .setAction(ImmutableList.of(new ActionBuilder()
+                    .setOrder(Integer.valueOf(0))
+                    .setAction(outputAction(id))
+                    .build()))
+                .build())
+            .build();
+    }
+        
     public static Instructions dropInstructions() {
         return new InstructionsBuilder()
-            .setInstruction(ImmutableList.of(new InstructionBuilder()
-                .setOrder(Integer.valueOf(0))
-                .setInstruction(new WriteActionsCaseBuilder()
-                    .setWriteActions(new WriteActionsBuilder()
-                        .setAction(ImmutableList.of(new ActionBuilder()
-                            .setOrder(Integer.valueOf(0))
-                            .setAction(new DropActionCaseBuilder()
-                                .setDropAction(new DropActionBuilder()
-                                    .build())
-                                .build())
-                            .build()))
-                        .build())
+        .setInstruction(ImmutableList.of(new InstructionBuilder()
+            .setOrder(Integer.valueOf(0))
+            .setInstruction(new WriteActionsCaseBuilder()
+                .setWriteActions(new WriteActionsBuilder()
+                    .setAction(ImmutableList.of(new ActionBuilder()
+                        .setOrder(Integer.valueOf(0))
+                        .setAction(dropAction())
+                        .build()))
                     .build())
-                .build()))
+                .build())
+            .build()))
+        .build();
+    }
+    
+    public static Action dropAction() {
+        return new DropActionCaseBuilder()
+            .setDropAction(new DropActionBuilder()
+                .build())
+            .build();
+    }
+    
+    public static Action outputAction(NodeConnectorId id) {
+        return new OutputActionCaseBuilder()
+            .setOutputAction(new OutputActionBuilder()
+                .setOutputNodeConnector(id)
+                .build())
             .build();
     }
     
index cc2a02be230636c9a5533e247b59523906acb7f8..8cbedb8aecffbbff32dcedf4c9a88182896d15e5 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
 import java.util.Map;
+import java.util.Set;
 
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
@@ -23,6 +24,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
@@ -59,6 +61,16 @@ public class PortSecurity extends FlowTable {
                      InstanceIdentifier<Table> tiid,
                      Map<String, FlowCtx> flowMap,
                      NodeId nodeId, Dirty dirty) {
+        // Allow traffic from tunnel and external ports
+        NodeConnectorId tunnelIf = ctx.switchManager.getTunnelPort(nodeId);
+        if (tunnelIf != null)
+            allowFromPort(t, tiid, flowMap, tunnelIf);
+        Set<NodeConnectorId> external = 
+                ctx.switchManager.getExternalPorts(nodeId);
+        for (NodeConnectorId extIf: external) {
+            allowFromPort(t, tiid, flowMap, extIf);
+        }
+
         // Default drop all
         dropFlow(t, tiid, flowMap, 1, null);
         
@@ -66,8 +78,6 @@ public class PortSecurity extends FlowTable {
         dropFlow(t, tiid, flowMap, 110, ARP);
         dropFlow(t, tiid, flowMap, 111, IPv4);
         dropFlow(t, tiid, flowMap, 112, IPv6);
-        
-        // XXX - TODO Allow traffic from tunnel ports and external ports
 
         for (Endpoint e : ctx.endpointManager.getEndpointsForNode(nodeId)) {
             OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
@@ -86,6 +96,26 @@ public class PortSecurity extends FlowTable {
         }
     }
     
+    private void allowFromPort(ReadWriteTransaction t,
+                               InstanceIdentifier<Table> tiid,
+                               Map<String, FlowCtx> flowMap,
+                               NodeConnectorId port) {
+        FlowId flowid = new FlowId(new StringBuilder()
+            .append("allow|")
+            .append(port.getValue())
+            .toString());
+        if (visit(flowMap, flowid.getValue())) {
+            FlowBuilder flowb = base()
+                .setId(flowid)
+                .setPriority(Integer.valueOf(200))
+                .setMatch(new MatchBuilder()
+                    .setInPort(port)
+                    .build())
+                .setInstructions(FlowUtils.gotoTableInstructions((short)(getTableId()+1)));
+            writeFlow(t, tiid, flowb.build());
+        }
+    }
+    
     private void dropFlow(ReadWriteTransaction t,
                           InstanceIdentifier<Table> tiid,
                           Map<String, FlowCtx> flowMap,
@@ -127,7 +157,7 @@ public class PortSecurity extends FlowTable {
                                                               null, null))
                     .setInPort(ofc.getNodeConnectorId())
                     .build())
-                .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)));
+                .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)));
 
             writeFlow(t, tiid, flowb.build());
         }
@@ -189,7 +219,7 @@ public class PortSecurity extends FlowTable {
                         .setLayer3Match(m)
                         .setInPort(ofc.getNodeConnectorId())
                         .build())
-                    .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)))
+                    .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)))
                     .build();
 
                 writeFlow(t, tiid, flow);
index 50c7576f067ffad1ad4a7c27e30bfae887a4ab3f..97541e4db78fe08bf8d5fe509a65901d13630ea5 100644 (file)
@@ -117,7 +117,7 @@ public class SourceMapper extends FlowTable {
                     .setInPort(ofc.getNodeConnectorId())
                     .build())
                 // XXX TODO set sepg, bd, fd, vrf into registers
-                .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)));
+                .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)));
             writeFlow(t, tiid, flowb.build());
         }
     }
index 0d3b87cc4de054eb356b7af37b38e9e78a778da6..21faf4c432acad2c1f5e76a9f93d014e91f2837a 100644 (file)
@@ -51,7 +51,7 @@ class PolicyCache {
                                         ConditionSet ep1Conds,
                                         TenantId ep2Tenant,
                                         EndpointGroupId ep2Group, 
-                                        ConditionSet ep2Conds) {
+                                        ConditionSet ep2Conds) {        
         EgKey k1 = new EgKey(ep1Tenant, ep1Group);
         EgKey k2 = new EgKey(ep2Tenant, ep2Group);
         Policy p = policy.get().get(k1, k2);
@@ -61,6 +61,28 @@ class PolicyCache {
         return result;
     }
     
+    /**
+     * Get the set of providers that have contracts with the consumer
+     * @param tenant the tenant ID for the endpoint group
+     * @param eg the endpoint group ID
+     */
+    protected Set<EgKey> getProvidersForConsumer(TenantId tenant,
+                                                 EndpointGroupId eg) {
+        EgKey k = new EgKey(tenant, eg);
+        return Collections.unmodifiableSet(policy.get().row(k).keySet());
+    }
+    
+    /**
+     * Get the set of providers that apply 
+     * @param tenant the tenant ID for the endpoint group
+     * @param eg the endpoint group ID
+     */
+    protected Set<EgKey> getConsumersForProvider(TenantId tenant,
+                                                 EndpointGroupId eg) {
+        EgKey k = new EgKey(tenant, eg);
+        return Collections.unmodifiableSet(policy.get().column(k).keySet());
+    }
+    
     /**
      * Atomically update the active policy and notify policy listeners 
      * of relevant changes
index 9b413f0a8105a4a20ababb6cb34afb27e37d76c5..b1b2293c09f6cbf792bd4151a0aaadb943b837e4 100644 (file)
@@ -165,7 +165,26 @@ public class PolicyResolver implements AutoCloseable {
         return policyCache.getPolicy(ep1Tenant, ep1Group, ep1Conds,
                                      ep2Tenant, ep2Group, ep2Conds);
     }
-
+    /**
+     * Get the set of providers that have contracts with the consumer
+     * @param tenant the tenant ID for the endpoint group
+     * @param eg the endpoint group ID
+     */
+    public Set<EgKey> getProvidersForConsumer(TenantId tenant,
+                                              EndpointGroupId eg) {
+        return policyCache.getProvidersForConsumer(tenant, eg);
+    }
+    
+    /**
+     * Get the set of providers that apply 
+     * @param tenant the tenant ID for the endpoint group
+     * @param eg the endpoint group ID
+     */
+    public Set<EgKey> getConsumersForProvider(TenantId tenant,
+                                              EndpointGroupId eg) {
+        return policyCache.getConsumersForProvider(tenant, eg);
+    }
+    
     /**
      * Get the normalized tenant for the given ID
      * @param tenant the tenant ID
@@ -250,13 +269,11 @@ public class PolicyResolver implements AutoCloseable {
         TenantContext context = resolvedTenants.get(tenantId);
         if (context == null) {
             ListenerRegistration<DataChangeListener> registration = null;
-            if (dataProvider != null) {
-                 registration = dataProvider
-                         .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
-                                                     TenantUtils.tenantIid(tenantId),
-                                                     new PolicyChangeListener(tenantId),
-                                                     DataChangeScope.SUBTREE);
-            }
+            registration = dataProvider
+                    .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                                                TenantUtils.tenantIid(tenantId),
+                                                new PolicyChangeListener(tenantId),
+                                                DataChangeScope.SUBTREE);
 
             context = new TenantContext(registration);
             TenantContext oldContext =
index 620fec4fbe114e798ee388b1292464f992ccc39b..19e65df353933ea11cc37eff3fb31a71622d8f06 100644 (file)
@@ -134,4 +134,34 @@ module ofoverlay {
         ext:augment-identifier "of-overlay-context-input";
         uses endpoint-location;
     }
+
+    augment "/inv:nodes/inv:node" {
+        ext:augment-identifier "of-overlay-node-config";
+
+        leaf tunnel-ip {
+            description 
+                "The IP address used for tunnel termination on this switch";
+            type inet:ip-address;
+        }
+        container ovsdb-config {
+            leaf ip {
+                description 
+                    "The IP address used to connect to OVSDB on this switch";
+                type inet:ip-address;
+            }
+            leaf port {
+                description 
+                    "The port number used to connect to OVSDB on this switch";
+                type inet:port-number;
+                default 6640;
+            }
+        }
+        leaf-list external-interfaces {
+            description 
+                "The names of the OpenFlow interfaces that are external and 
+                 lead to the underlay network.  Can be specified as a 
+                 regular expression.";
+            type string;
+        }
+    }
 }
diff --git a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/MockSwitchManager.java b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/MockSwitchManager.java
new file mode 100644 (file)
index 0000000..0c13cec
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
+
+import java.util.Set;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+
+/**
+ * Mock version of switch manager suitable for unit tests
+ */
+public class MockSwitchManager extends SwitchManager {
+
+    public MockSwitchManager() {
+        super(null, null);
+    }
+
+    public void addSwitch(NodeId node, 
+                          NodeConnectorId tunnelPort,
+                          Set<NodeConnectorId> externalPorts) {
+        SwitchState state = new SwitchState(node, tunnelPort, externalPorts);
+        state.status = SwitchStatus.READY;
+        switches.put(node, state);
+        for (SwitchListener listener : listeners) {
+            listener.switchReady(node);
+        }
+    }
+}
index 8d38e690f4e8166084de84c41fa7c396c02f2cb3..900dd66ae084c5313e0062f8fe6a0a1a2dc01cf0 100644 (file)
@@ -14,6 +14,7 @@ import java.util.Map;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockEndpointManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockPolicyManager;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockSwitchManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowCtx;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowTableCtx;
 import org.opendaylight.groupbasedpolicy.resolver.MockPolicyResolver;
@@ -47,9 +48,11 @@ import static org.mockito.Mockito.*;
 public class FlowTableTest {
     FlowTableCtx ctx;
     FlowTable table;
+    
     MockEndpointManager endpointManager;
     MockPolicyResolver policyResolver;
     MockPolicyManager policyManager;
+    MockSwitchManager switchManager;
     
     NodeId nodeId = new NodeId("openflow:1");
     InstanceIdentifier<Table> tiid;
@@ -65,11 +68,13 @@ public class FlowTableTest {
         endpointManager = new MockEndpointManager();
         policyResolver = new MockPolicyResolver();
         policyManager = new MockPolicyManager(policyResolver, endpointManager);
+        switchManager = new MockSwitchManager();
+        
         ctx = new FlowTableCtx(null, 
                                null, 
                                policyManager, 
                                policyResolver, 
-                               null
+                               switchManager
                                endpointManager, 
                                null);
     }
index 9ffe27897f9bc3eaf648c39d55e1bd117b9b7a27..ad955003f025181c12731cb6375fa932c55a8ccf 100644 (file)
@@ -12,6 +12,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -27,6 +28,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3AddressBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
@@ -35,6 +38,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import static org.junit.Assert.*;
 
@@ -67,7 +71,7 @@ public class PortSecurityTest extends FlowTableTest {
             Long etherType = null;
             if (f.getMatch() != null) {
                 etherType = f.getMatch().getEthernetMatch()
-                        .getEthernetType().getType().getValue().longValue();
+                        .getEthernetType().getType().getValue();
             }
             if (f.getMatch() == null ||
                 PortSecurity.ARP.equals(etherType) ||
@@ -84,6 +88,38 @@ public class PortSecurityTest extends FlowTableTest {
                                any(InstanceIdentifier.class), 
                                any(Flow.class));
     }
+
+    @Test
+    public void testNonLocalAllow() throws Exception {
+        switchManager
+            .addSwitch(new NodeId("openflow:1"), 
+                       new NodeConnectorId("openflow:1:1"), 
+                       ImmutableSet.of(new NodeConnectorId("openflow:1:2")));
+
+        ReadWriteTransaction t = dosync(null);
+        ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
+        verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION), 
+                      any(InstanceIdentifier.class), ac.capture());
+        
+        int count = 0;
+        HashMap<String, FlowCtx> flowMap = new HashMap<>();
+        Set<String> ncs = ImmutableSet.of("openflow:1:1", "openflow:1:2");
+        for (Flow f : ac.getAllValues()) {
+            flowMap.put(f.getId().getValue(), new FlowCtx(f));
+            if (f.getMatch() != null && f.getMatch().getInPort() != null &&
+                ncs.contains(f.getMatch().getInPort().getValue())) {
+                assertEquals(f.getInstructions(), 
+                             FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)));
+                count += 1;
+            }
+        }
+        assertEquals(2, count);
+
+        t = dosync(flowMap);
+        verify(t, never()).put(any(LogicalDatastoreType.class), 
+                               any(InstanceIdentifier.class), 
+                               any(Flow.class));
+    }
     
     @Test
     public void testL2() throws Exception {
@@ -93,8 +129,8 @@ public class PortSecurityTest extends FlowTableTest {
             .build();
        
         endpointManager.addEndpoint(ep);
-        ReadWriteTransaction t = dosync(null);
         
+        ReadWriteTransaction t = dosync(null);
         ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
         verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION), 
                       any(InstanceIdentifier.class), ac.capture());
@@ -112,7 +148,7 @@ public class PortSecurityTest extends FlowTableTest {
                 Objects.equals(ep.getAugmentation(OfOverlayContext.class).getNodeConnectorId(), 
                                f.getMatch().getInPort())) {
                 count += 1;
-                assertEquals(FlowUtils.gotoTable((short)(table.getTableId()+1)),
+                assertEquals(FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)),
                              f.getInstructions());
             }
         }
@@ -135,8 +171,8 @@ public class PortSecurityTest extends FlowTableTest {
             .build();
         
         endpointManager.addEndpoint(ep);
-        ReadWriteTransaction t = dosync(null);
         
+        ReadWriteTransaction t = dosync(null);
         ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
         verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION), 
                       any(InstanceIdentifier.class), ac.capture());
@@ -161,7 +197,7 @@ public class PortSecurityTest extends FlowTableTest {
                   Objects.equals(ep.getL3Address().get(1).getIpAddress().getIpv6Address().getValue(),
                                  ((Ipv6Match)f.getMatch().getLayer3Match()).getIpv6Source().getValue())))) {
                 count += 1;
-                assertEquals(FlowUtils.gotoTable((short)(table.getTableId()+1)),
+                assertEquals(FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)),
                              f.getInstructions());
             }
         }