Bug 1364: Preserve flow entries during container mode. 03/9003/2
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Tue, 15 Jul 2014 04:31:31 +0000 (13:31 +0900)
committerShigeru Yasuda <s-yasuda@da.jp.nec.com>
Tue, 15 Jul 2014 08:04:32 +0000 (17:04 +0900)
Flow entries in the default container should be preserved when the contoller
enters the container mode.

  * Ignore FLOW_REMOVED in the default container during container mode.
  * Data flow APIs behave as if no data flow is installed in the default
    container during container mode.

Change-Id: Ia999160aa123ea062f8d36af1eb072c44bf77e4c
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/Activator.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/GlobalResourceManager.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/RemovedFlowMatch.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/VTNManagerImpl.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/FlowModTaskTestBase.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestStub.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestUseVTNManagerBase.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VNodeEventTestBase.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java

index 80c64144c6871a087dd80a22c8ed96f909f254d8..188327bd5d2a14b39f8f0c6ac15a0cbce8899577 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
 import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
 import org.opendaylight.controller.connectionmanager.IConnectionManager;
+import org.opendaylight.controller.containermanager.IContainerManager;
 import org.opendaylight.controller.forwardingrulesmanager.
     IForwardingRulesManager;
 import org.opendaylight.controller.hosttracker.IfHostListener;
@@ -169,6 +170,12 @@ public class Activator extends ComponentActivatorAbstractBase {
             list.add(IFlowProgrammerListener.class.getName());
             if (containerName.equals(GlobalConstants.DEFAULT.toString())) {
                 list.add(IContainerListener.class.getName());
+
+                c.add(createServiceDependency().
+                      setService(IContainerManager.class).
+                      setCallbacks("setContainerManager",
+                                   "unsetContainerManager").
+                      setRequired(true));
             }
 
             // Export IVTNFlowDebugger only if "vtn.debug" system property
index 6f3e4bbf9da2c474100e8a71ff8ffe983f9767b3..4d13019353b9c04a3387632d0ae700f8866e8d4b 100644 (file)
@@ -2324,8 +2324,17 @@ public class GlobalResourceManager
         } catch (Exception e) {
             LOG.error(containerName + ": Failed to clean up resource", e);
         }
-    }
 
+        if (vtnManagers.size() == 1) {
+            // The controller quits the container mode.
+            // Some FLOW_REMOVED notifications might be ignored when the
+            // controller entered the container mode.
+            // So we need to clean up them.
+            VTNManagerImpl mgr =
+                vtnManagers.get(GlobalConstants.DEFAULT.toString());
+            mgr.cleanUpRemovedFlows();
+        }
+    }
 
     /**
      * {@inheritDoc}
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/RemovedFlowMatch.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/RemovedFlowMatch.java
new file mode 100644 (file)
index 0000000..003763a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.internal;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.opendaylight.vtn.manager.internal.cluster.VTNFlow;
+
+import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
+import org.opendaylight.controller.forwardingrulesmanager.
+    IForwardingRulesManager;
+
+/**
+ * {@link VTNFlowMatch} implementation which accepts VTN flows which are
+ * already removed.
+ */
+public class RemovedFlowMatch implements VTNFlowMatch {
+    /**
+     * Forwarding rules manager service.
+     */
+    private final IForwardingRulesManager  fwRulesManager;
+
+    /**
+     * Construct a new instance.
+     *
+     * @param frm  Forwarding rules manager service.
+     */
+    public RemovedFlowMatch(IForwardingRulesManager frm) {
+        fwRulesManager = frm;
+    }
+
+    /**
+     * Test if the specified VTN flow should be accepted or not.
+     *
+     * @param vflow  A {@link VTNFlow} instance to be tested.
+     * @return  {@code true} if the specified flow is already removed.
+     *          Otherwise {@code false}.
+     */
+    @Override
+    public boolean accept(VTNFlow vflow) {
+        HashSet<String> installed = new HashSet<String>();
+        String group = vflow.getGroupId().toString();
+        for (FlowEntry fent: fwRulesManager.getFlowEntriesForGroup(group)) {
+            installed.add(fent.getFlowName());
+        }
+
+        List<FlowEntry> entries = vflow.getFlowEntries();
+        if (entries.size() != installed.size()) {
+            return true;
+        }
+
+        for (FlowEntry fent: entries) {
+            if (!installed.contains(fent.getFlowName())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getDescription() {
+        return "removed";
+    }
+}
index 99a70e1d1f5ea0aa9f34b0866fbe899a65815186..2f0a796cfcc8709b9d93f2e70f07cabaf64eb073 100644 (file)
@@ -138,7 +138,6 @@ import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
 import org.opendaylight.controller.sal.utils.EtherTypes;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.NetUtils;
-import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.sal.utils.StatusCode;
 import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
@@ -366,6 +365,16 @@ public class VTNManagerImpl
      */
     private IConnectionManager  connectionManager;
 
+    /**
+     * Container manager service instance.
+     *
+     * <p>
+     *   Note that {@code null} is set unless this instance is associated with
+     *   the default container.
+     * </p>
+     */
+    private IContainerManager  containerManager;
+
     /**
      * Host listeners.
      */
@@ -682,11 +691,9 @@ public class VTNManagerImpl
         String root = GlobalConstants.STARTUPHOME.toString();
         vtnConfig = new VTNConfig(root, cname);
 
-        if (cname.equals(GlobalConstants.DEFAULT.toString())) {
-            IContainerManager ctMgr = (IContainerManager)ServiceHelper.
-                getGlobalInstance(IContainerManager.class, this);
-            inContainerMode =
-                (ctMgr != null && ctMgr.hasNonDefaultContainer());
+        if (containerManager != null) {
+            assert containerName.equals(GlobalConstants.DEFAULT.toString());
+            inContainerMode = containerManager.inContainerMode();
         } else {
             inContainerMode = false;
         }
@@ -1802,6 +1809,41 @@ public class VTNManagerImpl
         return connectionManager;
     }
 
+    /**
+     * Invoked when a container manager service is registered.
+     *
+     * @param service  Container manager service.
+     */
+    void setContainerManager(IContainerManager service) {
+        LOG.trace("{}: Set container manager service: {}", containerName,
+                  service);
+        containerManager = service;
+    }
+
+    /**
+     * Invoked when a container manager service is unregistered.
+     *
+     * @param service  Container manager service.
+     */
+    void unsetContainerManager(IContainerManager service) {
+        if (containerManager == service) {
+            LOG.trace("{}: Unset container manager service: {}",
+                      containerName, service);
+            containerManager = null;
+        }
+    }
+
+    /**
+     * Return container manager service instance.
+     *
+     * @return  Container manager service.
+     *          Note that {@code null} is always returned unless this instance
+     *          is associated with the default container.
+     */
+    public IContainerManager getContainerManager() {
+        return containerManager;
+    }
+
     /**
      * Invoked when a host listener is registered.
      *
@@ -2210,6 +2252,18 @@ public class VTNManagerImpl
         }
     }
 
+    /**
+     * Collect inactive flows and remove them in background.
+     */
+    public void cleanUpRemovedFlows() {
+        if (clusterService.amICoordinator()) {
+            RemovedFlowMatch fmatch = new RemovedFlowMatch(fwRuleManager);
+            for (VTNFlowDatabase fdb: vtnFlowMap.values()) {
+                fdb.removeFlows(this, fmatch);
+            }
+        }
+    }
+
     /**
      * Determine whether the given node connector is associated with the
      * physical switch port at the edge of the SDN network.
@@ -4153,9 +4207,6 @@ public class VTNManagerImpl
             }
         } else if (arpHandler == null) {
             // Inactivate VTN, and start ARP handler emulator.
-            for (VTNFlowDatabase fdb: vtnFlowMap.values()) {
-                VTNThreadData.removeFlows(this, fdb);
-            }
             arpHandler = new ArpHandler(this);
             if (sync) {
                 notifyListeners(false);
@@ -5508,6 +5559,13 @@ public class VTNManagerImpl
     public List<DataFlow> getDataFlows(VTenantPath path, DataFlow.Mode mode,
                                        DataFlowFilter filter)
         throws VTNException {
+        if (inContainerMode) {
+            // No flow entry is active in container mode.
+            // It's harmless to access inContainerMode flag without holding
+            // rmLock.
+            return new ArrayList<DataFlow>(0);
+        }
+
         // We should not acquire lock here because succeeding method call may
         // make requests to get flow statistics. Synchronization will be done
         // by VTNFlowDatabase appropriately.
@@ -5535,6 +5593,13 @@ public class VTNManagerImpl
     public DataFlow getDataFlow(VTenantPath path, long flowId,
                                 DataFlow.Mode mode)
         throws VTNException {
+        if (inContainerMode) {
+            // No flow entry is active in container mode.
+            // It's harmless to access inContainerMode flag without holding
+            // rmLock.
+            return null;
+        }
+
         // We should not acquire lock here because succeeding method call may
         // make requests to get flow statistics. Synchronization will be done
         // by VTNFlowDatabase appropriately.
@@ -5553,6 +5618,13 @@ public class VTNManagerImpl
      */
     @Override
     public int getDataFlowCount(VTenantPath path) throws VTNException {
+        if (inContainerMode) {
+            // No flow entry is active in container mode.
+            // It's harmless to access inContainerMode flag without holding
+            // rmLock.
+            return 0;
+        }
+
         VTNFlowDatabase fdb = getTenantFlowDB(path);
         return fdb.getFlowCount();
     }
@@ -7211,6 +7283,20 @@ public class VTNManagerImpl
      */
     @Override
     public void flowRemoved(Node node, Flow flow) {
+        if (containerManager != null && containerManager.inContainerMode()) {
+            // The given flow was removed by forwarding rule manager, and it
+            // will be restored when the controller exits the container mode.
+            // Note that we can not use inContainerMode variable here because
+            // containerModeUpdated() handler for FRM may be called before
+            // the VTN Manager. Although this code may miss FLOW_REMOVED
+            // notifications actually sent by OF switch, they will be fixed
+            // when the controller quits the container mode.
+            assert containerName.equals(GlobalConstants.DEFAULT.toString());
+            LOG.trace("{}: Ignore FLOW_REMOVED during container mode: " +
+                      "node={}, flow={}", containerName, node, flow);
+            return;
+        }
+
         LOG.trace("{}: flowRemoved() called: node={}, flow={}",
                   containerName, node, flow);
 
index 462bf1b949fb67bc5bee1819f108c8d98b47de1d..51be585f7c26f58ff33ae3534bda620954833347 100644 (file)
@@ -406,6 +406,7 @@ public class FlowModTaskTestBase extends TestUseVTNManagerBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(cm);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
index e01056587d4a4a851159c38c59fb09bdaf221850..3cea1700ade38336fb54fc317a8400374c3de990 100644 (file)
@@ -40,6 +40,9 @@ import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
 import org.opendaylight.controller.clustering.services.IClusterServices.cacheMode;
 import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
 import org.opendaylight.controller.connectionmanager.IConnectionManager;
+import org.opendaylight.controller.containermanager.ContainerConfig;
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+import org.opendaylight.controller.containermanager.IContainerManager;
 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
@@ -72,6 +75,7 @@ import org.opendaylight.controller.sal.packet.LinkEncap;
 import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.RawPacket;
 import org.opendaylight.controller.sal.routing.IRouting;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.NodeCreator;
@@ -99,7 +103,7 @@ public class TestStub
     implements IClusterGlobalServices, IClusterContainerServices,
                ISwitchManager, ITopologyManager, IDataPacketService, IRouting,
                IForwardingRulesManager, IfIptoHost, IConnectionManager,
-               IfHostListener {
+               IfHostListener, IContainerManager {
     /**
      * The name of the cluster cache which keeps revision identifier of
      * configuration per mapping type.
@@ -1401,6 +1405,102 @@ public class TestStub
         return null;
     }
 
+    // IContainerManager
+
+    @Override
+    public Status addContainer(ContainerConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeContainer(ContainerConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeContainer(String containerName) {
+        return null;
+    }
+
+    @Override
+    public Status addContainerEntry(String containerName,
+                                    List<String> portList) {
+        return null;
+    }
+
+    @Override
+    public Status removeContainerEntry(String containerName,
+                                       List<String> portList) {
+        return null;
+    }
+
+    @Override
+    public Status addContainerFlows(String containerName,
+                                    List<ContainerFlowConfig> configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName,
+                                       List<ContainerFlowConfig> configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName, Set<String> name) {
+        return null;
+    }
+
+    @Override
+    public List<ContainerConfig> getContainerConfigList() {
+        return null;
+    }
+
+    @Override
+    public ContainerConfig getContainerConfig(String containerName) {
+        return null;
+    }
+
+    @Override
+    public List<String> getContainerNameList() {
+        return null;
+    }
+
+    @Override
+    public boolean doesContainerExist(String ContainerId) {
+        return GlobalConstants.DEFAULT.toString().equals(ContainerId);
+    }
+
+    @Override
+    public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
+        return null;
+    }
+
+    @Override
+    public List<ContainerFlowConfig> getContainerFlows(String containerName) {
+        return null;
+    }
+
+    @Override
+    public List<String> getContainerFlowNameList(String containerName) {
+        return null;
+    }
+
+    @Override
+    public boolean hasNonDefaultContainer() {
+        return false;
+    }
+
+    @Override
+    public List<String> getContainerNames() {
+        return null;
+    }
+
+    @Override
+    public boolean inContainerMode() {
+        return false;
+    }
+
     // additional method for control stub
     public void addEdge(Edge edge) {
         Map<Node, List<Edge>> map = nodeEdges.get(edge.getTailNodeConnector().getNode());
index b2c84e9dcb803028eefc6f9733fbb9efde4f55bf..ca398e28917b7dd526d7d138a3c8f54d7152659a 100644 (file)
@@ -14,6 +14,7 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Dictionary;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -56,6 +57,7 @@ import org.opendaylight.controller.clustering.services.CacheConfigException;
 import org.opendaylight.controller.clustering.services.CacheExistException;
 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.containermanager.IContainerManager;
 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
 import org.opendaylight.controller.hosttracker.IfHostListener;
 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
@@ -135,6 +137,7 @@ public class TestUseVTNManagerBase extends TestBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
         if (stubObj.isHostTrackerEnabled()) {
             vtnMgr.addHostListener(stubObj);
@@ -153,6 +156,18 @@ public class TestUseVTNManagerBase extends TestBase {
      * startup VTNManager.
      */
     protected void startVTNManager(ComponentImpl c) {
+        Dictionary<?, ?> props = c.getServiceProperties();
+        if (props != null) {
+            String name = (String)props.get("containerName");
+            IContainerManager ctmgr = vtnMgr.getContainerManager();
+            if (GlobalConstants.DEFAULT.toString().equals(name)) {
+                if (ctmgr == null) {
+                    vtnMgr.setContainerManager(stubObj);
+                }
+            } else if (ctmgr != null) {
+                vtnMgr.unsetContainerManager(ctmgr);
+            }
+        }
         vtnMgr.init(c);
         vtnMgr.clearDisabledNode();
     }
@@ -178,6 +193,7 @@ public class TestUseVTNManagerBase extends TestBase {
         vtnMgr.setHostTracker(stub);
         vtnMgr.setForwardingRuleManager(stub);
         vtnMgr.setConnectionManager(stub);
+        vtnMgr.setContainerManager(stub);
         startVTNManager(c);
         if (stub.isHostTrackerEnabled()) {
             vtnMgr.addHostListener(stub);
index d66554a0424737969f3e5c23864c79961fba67b4..d3625832fb2c98ec8ec6d5f28180d289365e2c95 100644 (file)
@@ -62,6 +62,7 @@ public class VNodeEventTestBase extends TestUseVTNManagerBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
index f2772a5961a5609d65f7ad9a318251b744ece37d..1f9690185a361e8b4e3ca4e7f7c98759a2a698e9 100644 (file)
@@ -145,6 +145,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
index 35b369cba706f62a84eb2eb06a297697553046c8..901c58282990ae898fb3ede7bb9548607f0d3bbd 100644 (file)
@@ -564,6 +564,7 @@ public class VTNManagerImplDisableNodesTest extends TestBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         vtnMgr.init(c);
         vtnMgr.clearDisabledNode();
     }
index e997e388b4c01c52da1454d52309ff2e9b4d3148..81be40d359bb6572c70223a88191124a18803156 100644 (file)
@@ -81,6 +81,7 @@ import org.opendaylight.vtn.manager.internal.cluster.VlanMapPath;
 
 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.connectionmanager.IConnectionManager;
+import org.opendaylight.controller.containermanager.IContainerManager;
 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
 import org.opendaylight.controller.hosttracker.IfHostListener;
@@ -693,6 +694,33 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         assertSame(org, mgr.getConnectionManager());
     }
 
+    /**
+     * Test method for
+     * {@link VTNManagerImpl#setContainerManager(IContainerManager)},
+     * {@link VTNManagerImpl#unsetContainerManager(IContainerManager)},
+     * {@link VTNManagerImpl#getContainerManager()}
+     * .
+     */
+    @Test
+    public void testSetUnsetContainerManager() {
+        VTNManagerImpl mgr = vtnMgr;
+        IContainerManager org = mgr.getContainerManager();
+        TestStub stub = new TestStub();
+        TestStub stub2 = new TestStub();
+
+        mgr.setContainerManager(stub);
+        assertSame(stub, mgr.getContainerManager());
+
+        mgr.unsetContainerManager(stub2);
+        assertSame(stub, mgr.getContainerManager());
+
+        mgr.unsetContainerManager(stub);
+        assertNull(mgr.getContainerManager());
+
+        mgr.setContainerManager(org);
+        assertSame(org, mgr.getContainerManager());
+    }
+
     /**
      * Test method for
      * {@link VTNManagerImpl#setResourceManager(IVTNResourceManager)},