Bug 1364: Preserve flow entries during container mode. 00/11800/1
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>
Wed, 8 Oct 2014 07:42:40 +0000 (16:42 +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>
(cherry picked from commit ffe775d4991d74aa9a888b36002a7bcc3f9f2819)

13 files changed:
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/VTNFlowDatabase.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/VTNFlowMatch.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 a61628139a3604dd580150ac91c60bfd48a4fcf1..705bb5cf825f5495f56538bfd6829a7ea72b2e6a 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;
@@ -168,6 +169,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 91f66e7a7f1cfce98fe7c3ded664bc3a0a5d80d6..e55b6e5d46fe90d5117fafc0379fea7e7963d2b4 100644 (file)
@@ -525,6 +525,15 @@ public class GlobalResourceManager
         for (PortVlan pvlan: pvSet) {
             portMaps.remove(pvlan);
         }
+
+        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(0);
+            mgr.cleanUpRemovedFlows();
+        }
     }
 
     // ICoordinatorChangeAware
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 40aedbc909eca25a6ca4722a6bd3534acda4bf4a..6388d0c2854b7d474495f424b26005de536ebe13 100644 (file)
@@ -586,6 +586,53 @@ public class VTNFlowDatabase {
         return collector.uninstall(mgr);
     }
 
+    /**
+     * Remove all VTN flows accepted by the specified {@link VTNFlowMatch}
+     * instance.
+     *
+     * <p>
+     *   Flow uninstallation will be executed in background.
+     *   The caller can wait for completion of flow uninstallation by using
+     *   returned {@link FlowRemoveTask} object.
+     * </p>
+     *
+     * @param mgr     VTN Manager service.
+     * @param fmatch  A {@link VTNFlowMatch} instance which determines VTN
+     *                flows to be removed.
+     *                Specifying {@code null} results in undefined behavior.
+     * @return  A {@link FlowRemoveTask} object that will execute the actual
+     *          work is returned. {@code null} is returned if there is no flow
+     *          entry to be removed.
+     */
+    public synchronized FlowRemoveTask removeFlows(VTNManagerImpl mgr,
+                                                   VTNFlowMatch fmatch) {
+        FlowCollector collector = new FlowCollector();
+        for (Iterator<VTNFlow> it = vtnFlows.values().iterator();
+             it.hasNext();) {
+            VTNFlow vflow = it.next();
+            if (fmatch.accept(vflow)) {
+                FlowGroupId gid = vflow.getGroupId();
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("{}:{}: Remove VTN flow accepted by filter" +
+                              "({}): group={}", mgr.getContainerName(),
+                              tenantName, fmatch.getDescription(), gid);
+                }
+
+                // Remove this VTN flow from the database.
+                groupFlows.remove(gid.getEventId());
+                removeNodeIndex(vflow);
+                removePortIndex(vflow);
+                it.remove();
+
+                // Collect flow entries to be uninstalled.
+                collector.collect(mgr, vflow);
+            }
+        }
+
+        // Uninstall flow entries in background.
+        return collector.uninstall(mgr);
+    }
+
     /**
      * Create indices for the specified VTN flow.
      *
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/VTNFlowMatch.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/VTNFlowMatch.java
new file mode 100644 (file)
index 0000000..d9cbab5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 org.opendaylight.vtn.manager.internal.cluster.VTNFlow;
+
+/**
+ * {@code VTNFlowMatch} provides interfaces to be implemented by classes which
+ * search for specific flow entries.
+ */
+public interface VTNFlowMatch {
+    /**
+     * 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 should be accepted.
+     *          {@code false} if the specified flow should be filtered out.
+     */
+    boolean accept(VTNFlow vflow);
+
+    /**
+     * Return a brief description about this filter.
+     *
+     * @return  A brief description about this filter.
+     */
+    String getDescription();
+}
index d54a7efcc90f25e06c34e31ebe480e0b2dfc356e..cabc4be28119675d0d7e48e8247252d9bb6cda27 100644 (file)
@@ -125,7 +125,6 @@ import org.opendaylight.controller.sal.utils.IObjectReader;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.ObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectWriter;
-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.switchmanager.IInventoryListener;
@@ -333,6 +332,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.
      */
@@ -733,11 +742,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;
         }
@@ -1781,6 +1788,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.
      *
@@ -2140,6 +2182,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.
@@ -3643,9 +3697,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);
@@ -5442,6 +5493,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 901d77ac33c581b6d172b8551d6026b57f2d4ce1..90ba58f6f008629b94c65c26aa1d05c97024db5a 100644 (file)
@@ -391,6 +391,7 @@ public class FlowModTaskTestBase extends TestUseVTNManagerBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(cm);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
index 0b393d02ae2b8d055c62c1d7360ea0df940f5678..b584ea62bae5c61ee9510b0a97b9bfafb9966bf9 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;
@@ -71,6 +74,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;
@@ -94,10 +98,11 @@ import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
  *   (other is not implemented yet.)
  * </p>
  */
-public class TestStub implements IClusterGlobalServices, IClusterContainerServices,
-    ISwitchManager, ITopologyManager, IDataPacketService, IRouting,
-    IForwardingRulesManager, IfIptoHost, IConnectionManager {
-
+public class TestStub
+    implements IClusterGlobalServices, IClusterContainerServices,
+               ISwitchManager, ITopologyManager, IDataPacketService, IRouting,
+               IForwardingRulesManager, IfIptoHost, IConnectionManager,
+               IContainerManager {
     /**
      * Active cluster cache transactions per thread.
      */
@@ -1332,6 +1337,102 @@ public class TestStub implements IClusterGlobalServices, IClusterContainerServic
         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 2694fed3c2ba5ba49b60cf002348150b422962dc..e23e57e75b6696293226603f8794b63decabe853 100644 (file)
@@ -12,6 +12,7 @@ package org.opendaylight.vtn.manager.internal;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Dictionary;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Hashtable;
@@ -27,6 +28,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;
@@ -98,6 +100,7 @@ public class TestUseVTNManagerBase extends TestBase {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
@@ -113,6 +116,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();
     }
@@ -135,6 +150,7 @@ public class TestUseVTNManagerBase extends TestBase {
         vtnMgr.setHostTracker(stub);
         vtnMgr.setForwardingRuleManager(stub);
         vtnMgr.setConnectionManager(stub);
+        vtnMgr.setContainerManager(stub);
         startVTNManager(c);
     }
 
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 38d911268a32bb2f4afa0a70a2768f8cc16b12cb..dc920447bda711c32a7b6f86e96c671e63249f27 100644 (file)
@@ -136,6 +136,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         vtnMgr.setHostTracker(stubObj);
         vtnMgr.setForwardingRuleManager(stubObj);
         vtnMgr.setConnectionManager(stubObj);
+        vtnMgr.setContainerManager(stubObj);
         startVTNManager(c);
     }
 
index 70e8b3f438206417614c5150c4a4ec6d8ad6feae..fddc2d70e208188ff926ce5797aa2e9062b9763c 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 f1eea89b915165d37a89a2add3d003045cd27dc9..35820c8b10e83a436c928503b035edbea00df866 100644 (file)
@@ -36,6 +36,7 @@ import org.junit.experimental.categories.Category;
 
 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;
@@ -652,6 +653,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)},