X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fprotocol_plugins%2Fopenflow%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fprotocol_plugin%2Fopenflow%2Finternal%2FInventoryServiceShim.java;h=f4843cf8283f36f2dc4ae957c2e46c42d363e4d6;hp=b522063ad73fbc5bbf6239bfd829e0d26b065710;hb=43767bb962ade9659c8aa7eb0e02d412a7a54db2;hpb=e326757873804be369d20efd0b54467871094c06 diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java index b522063ad7..f4843cf828 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.protocol_plugin.openflow.internal; +import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -16,31 +17,39 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener; import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimInternalListener; +import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsListener; import org.opendaylight.controller.protocol_plugin.openflow.core.IController; import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener; import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch; import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener; -import org.opendaylight.controller.sal.core.Actions; +import org.opendaylight.controller.sal.action.SupportedFlowActions; +import org.opendaylight.controller.sal.connection.ConnectionLocality; +import org.opendaylight.controller.sal.connection.IPluginOutConnectionService; import org.opendaylight.controller.sal.core.Buffers; import org.opendaylight.controller.sal.core.Capabilities; -import org.opendaylight.controller.sal.core.ConstructionException; import org.opendaylight.controller.sal.core.ContainerFlow; +import org.opendaylight.controller.sal.core.Description; +import org.opendaylight.controller.sal.core.IContainerAware; import org.opendaylight.controller.sal.core.IContainerListener; +import org.opendaylight.controller.sal.core.MacAddress; import org.opendaylight.controller.sal.core.Node; -import org.opendaylight.controller.sal.core.Node.NodeIDType; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.core.Tables; import org.opendaylight.controller.sal.core.TimeStamp; import org.opendaylight.controller.sal.core.UpdateType; import org.opendaylight.controller.sal.utils.GlobalConstants; +import org.opendaylight.controller.sal.utils.NodeCreator; import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPortStatus; import org.openflow.protocol.OFPortStatus.OFPortReason; import org.openflow.protocol.OFType; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.protocol.statistics.OFStatistics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,17 +57,22 @@ import org.slf4j.LoggerFactory; * The class describes a shim layer that bridges inventory events from Openflow * core to various listeners. The notifications are filtered based on container * configurations. - * - * + * + * */ public class InventoryServiceShim implements IContainerListener, - IMessageListener, ISwitchStateListener { + IMessageListener, ISwitchStateListener, IOFStatisticsListener, IContainerAware { protected static final Logger logger = LoggerFactory .getLogger(InventoryServiceShim.class); private IController controller = null; - private ConcurrentMap inventoryShimInternalListeners = new ConcurrentHashMap(); - private List inventoryShimExternalListeners = new CopyOnWriteArrayList(); - private ConcurrentMap> containerMap = new ConcurrentHashMap>(); + private final ConcurrentMap inventoryShimInternalListeners = new ConcurrentHashMap(); + private final Set globalInventoryShimInternalListeners = new HashSet(); + private final List inventoryShimExternalListeners = new CopyOnWriteArrayList(); + private final ConcurrentMap> nodeConnectorContainerMap = new ConcurrentHashMap>(); + private final ConcurrentMap> nodeContainerMap = new ConcurrentHashMap>(); + private final ConcurrentMap> nodeConnectorProps = new ConcurrentHashMap>(); + private final ConcurrentMap> nodeProps = new ConcurrentHashMap>(); + private IPluginOutConnectionService connectionOutService; void setController(IController s) { this.controller = s; @@ -70,6 +84,20 @@ public class InventoryServiceShim implements IContainerListener, } } + void setInventoryShimGlobalInternalListener(Map props, + IInventoryShimInternalListener s) { + if ((this.globalInventoryShimInternalListeners != null)) { + this.globalInventoryShimInternalListeners.add(s); + } + } + + void unsetInventoryShimGlobalInternalListener(Map props, + IInventoryShimInternalListener s) { + if ((this.globalInventoryShimInternalListeners != null)) { + this.globalInventoryShimInternalListeners.remove(s); + } + } + void setInventoryShimInternalListener(Map props, IInventoryShimInternalListener s) { if (props == null) { @@ -98,7 +126,7 @@ public class InventoryServiceShim implements IContainerListener, } String containerName = (String) props.get("containerName"); if (containerName == null) { - logger.error("unsetInventoryShimInternalListener containerName not supplied"); + logger.error("setInventoryShimInternalListener containerName not supplied"); return; } if ((this.inventoryShimInternalListeners != null) @@ -113,7 +141,7 @@ public class InventoryServiceShim implements IContainerListener, } void setInventoryShimExternalListener(IInventoryShimExternalListener s) { - logger.trace("Set inventoryShimExternalListener"); + logger.trace("Set inventoryShimExternalListener {}", s); if ((this.inventoryShimExternalListeners != null) && !this.inventoryShimExternalListeners.contains(s)) { this.inventoryShimExternalListeners.add(s); @@ -121,16 +149,27 @@ public class InventoryServiceShim implements IContainerListener, } void unsetInventoryShimExternalListener(IInventoryShimExternalListener s) { + logger.trace("Unset inventoryShimExternalListener {}", s); if ((this.inventoryShimExternalListeners != null) && this.inventoryShimExternalListeners.contains(s)) { this.inventoryShimExternalListeners.remove(s); } } + void setIPluginOutConnectionService(IPluginOutConnectionService s) { + connectionOutService = s; + } + + void unsetIPluginOutConnectionService(IPluginOutConnectionService s) { + if (connectionOutService == s) { + connectionOutService = null; + } + } + /** * Function called by the dependency manager when all the required * dependencies are satisfied - * + * */ void init() { this.controller.addMessageListener(OFType.PORT_STATUS, this); @@ -149,75 +188,90 @@ public class InventoryServiceShim implements IContainerListener, * Function called by the dependency manager when at least one dependency * become unsatisfied or when the component is shutting down because for * example bundle is being stopped. - * + * */ void destroy() { this.controller.removeMessageListener(OFType.PACKET_IN, this); this.controller.removeSwitchStateListener(this); this.inventoryShimInternalListeners.clear(); - this.containerMap.clear(); + this.nodeConnectorContainerMap.clear(); + this.nodeContainerMap.clear(); + this.globalInventoryShimInternalListeners.clear(); this.controller = null; } @Override public void receive(ISwitch sw, OFMessage msg) { - try { - if (msg instanceof OFPortStatus) { - handlePortStatusMessage(sw, (OFPortStatus) msg); - } - } catch (ConstructionException e) { - logger.error("",e); + if (msg instanceof OFPortStatus) { + handlePortStatusMessage(sw, (OFPortStatus) msg); } return; } - protected void handlePortStatusMessage(ISwitch sw, OFPortStatus m) - throws ConstructionException { - Node node = new Node(NodeIDType.OPENFLOW, sw.getId()); - NodeConnector nodeConnector = PortConverter.toNodeConnector(m.getDesc() - .getPortNumber(), node); - UpdateType type = null; + protected void handlePortStatusMessage(ISwitch sw, OFPortStatus m) { + Node node = NodeCreator.createOFNode(sw.getId()); + NodeConnector nodeConnector = PortConverter.toNodeConnector( + m.getDesc().getPortNumber(), node); + // get node connector properties + Set props = InventoryServiceHelper.OFPortToProps(m.getDesc()); + UpdateType type = null; if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) { type = UpdateType.ADDED; + nodeConnectorProps.put(nodeConnector, props); } else if (m.getReason() == (byte) OFPortReason.OFPPR_DELETE.ordinal()) { type = UpdateType.REMOVED; + nodeConnectorProps.remove(nodeConnector); } else if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) { type = UpdateType.CHANGED; + nodeConnectorProps.put(nodeConnector, props); } logger.trace("handlePortStatusMessage {} type {}", nodeConnector, type); if (type != null) { - // get node connector properties - Set props = InventoryServiceHelper.OFPortToProps(m - .getDesc()); notifyInventoryShimListener(nodeConnector, type, props); } } @Override public void switchAdded(ISwitch sw) { - if (sw == null) + if (sw == null) { return; + } + Node node = NodeCreator.createOFNode(sw.getId()); + if ((nodeProps.get(node) != null) && (connectionOutService.isLocal(node))) { + logger.debug("Ignore switchAdded {}", sw); + return; + } // Add all the nodeConnectors of this switch Map> ncProps = InventoryServiceHelper .OFSwitchToProps(sw); for (Map.Entry> entry : ncProps.entrySet()) { - notifyInventoryShimListener(entry.getKey(), UpdateType.ADDED, - entry.getValue()); + Set props = new HashSet(); + Set prop = entry.getValue(); + if (prop != null) { + props.addAll(prop); + } + nodeConnectorProps.put(entry.getKey(), props); + notifyInventoryShimListener(entry.getKey(), UpdateType.ADDED, entry.getValue()); } // Add this node - addNode(sw); + if (connectionOutService.getLocalityStatus(node) != ConnectionLocality.NOT_CONNECTED) { + addNode(sw); + } else { + logger.debug("Skipping node addition due to Connectivity Status : {}", connectionOutService.getLocalityStatus(node).name()); + } } @Override public void switchDeleted(ISwitch sw) { - if (sw == null) + if (sw == null) { return; + } removeNode(sw); } @@ -230,6 +284,8 @@ public class InventoryServiceShim implements IContainerListener, @Override public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) { + logger.debug("tagUpdated: {} type {} for container {}", new Object[] { + n, t, containerName }); } @Override @@ -238,56 +294,80 @@ public class InventoryServiceShim implements IContainerListener, } @Override - public void nodeConnectorUpdated(String containerName, NodeConnector p, - UpdateType t) { - if (this.containerMap == null) { - logger.error("containerMap is NULL"); - return; + public void nodeConnectorUpdated(String containerName, NodeConnector nc, UpdateType t) { + logger.debug("nodeConnectorUpdated: {} type {} for container {}", new Object[] { nc, t, containerName }); + Node node = nc.getNode(); + Set ncContainers = this.nodeConnectorContainerMap.get(nc); + Set nodeContainers = this.nodeContainerMap.get(node); + if (ncContainers == null) { + ncContainers = new CopyOnWriteArraySet(); } - List containers = this.containerMap.get(p); - if (containers == null) { - containers = new CopyOnWriteArrayList(); + if (nodeContainers == null) { + nodeContainers = new CopyOnWriteArraySet(); } - boolean updateMap = false; + boolean notifyNodeUpdate = false; + switch (t) { case ADDED: - if (!containers.contains(containerName)) { - containers.add(containerName); - updateMap = true; + if (ncContainers.add(containerName)) { + this.nodeConnectorContainerMap.put(nc, ncContainers); + } + if (nodeContainers.add(containerName)) { + this.nodeContainerMap.put(node, nodeContainers); + notifyNodeUpdate = true; } break; case REMOVED: - if (containers.contains(containerName)) { - containers.remove(containerName); - updateMap = true; + if (ncContainers.remove(containerName)) { + if (ncContainers.isEmpty()) { + // Do cleanup to reduce memory footprint if no + // elements to be tracked + this.nodeConnectorContainerMap.remove(nc); + } else { + this.nodeConnectorContainerMap.put(nc, ncContainers); + } + } + boolean nodeContainerUpdate = true; + for (NodeConnector ncContainer : nodeConnectorContainerMap.keySet()) { + if ((ncContainer.getNode().equals(node)) && (nodeConnectorContainerMap.get(ncContainer).contains(containerName))) { + nodeContainerUpdate = false; + break; + } + } + if (nodeContainerUpdate) { + nodeContainers.remove(containerName); + notifyNodeUpdate = true; + if (nodeContainers.isEmpty()) { + this.nodeContainerMap.remove(node); + } else { + this.nodeContainerMap.put(node, nodeContainers); + } } break; case CHANGED: break; } - if (updateMap) { - if (containers.isEmpty()) { - // Do cleanup to reduce memory footprint if no - // elements to be tracked - this.containerMap.remove(p); - } else { - this.containerMap.put(p, containers); - } - } + Set nodeProp = nodeProps.get(node); + if (nodeProp == null) { + return; + } + Set ncProp = nodeConnectorProps.get(nc); // notify InventoryService - notifyInventoryShimInternalListener(containerName, p, t, null); + notifyInventoryShimInternalListener(containerName, nc, t, ncProp); + + if (notifyNodeUpdate) { + notifyInventoryShimInternalListener(containerName, node, t, nodeProp); + } } - private void notifyInventoryShimExternalListener(Node node, - UpdateType type, Set props) { + private void notifyInventoryShimExternalListener(Node node, UpdateType type, Set props) { for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) { s.updateNode(node, type, props); } } - private void notifyInventoryShimExternalListener( - NodeConnector nodeConnector, UpdateType type, Set props) { + private void notifyInventoryShimExternalListener(NodeConnector nodeConnector, UpdateType type, Set props) { for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) { s.updateNodeConnector(nodeConnector, type, props); } @@ -295,89 +375,122 @@ public class InventoryServiceShim implements IContainerListener, private void notifyInventoryShimInternalListener(String container, NodeConnector nodeConnector, UpdateType type, Set props) { - IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners - .get(container); + IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners.get(container); if (inventoryShimInternalListener != null) { - inventoryShimInternalListener.updateNodeConnector(nodeConnector, - type, props); - logger.trace( - "notifyInventoryShimInternalListener {} type {} for container {}", - nodeConnector, type, container); + inventoryShimInternalListener.updateNodeConnector(nodeConnector, type, props); + logger.trace("notifyInventoryShimInternalListener {} type {} for container {}", new Object[] { + nodeConnector, type, container }); } } /* * Notify all internal and external listeners */ - private void notifyInventoryShimListener(NodeConnector nodeConnector, - UpdateType type, Set props) { - // Always notify default InventoryService. Store properties in default - // one. - notifyInventoryShimInternalListener(GlobalConstants.DEFAULT.toString(), - nodeConnector, type, props); - - // Now notify other containers - List containers = containerMap.get(nodeConnector); - if (containers != null) { + private void notifyInventoryShimListener(NodeConnector nodeConnector, UpdateType type, Set props) { + + //establish locality before notifying + boolean isNodeLocal; + if (type == UpdateType.REMOVED){ + //if removing get the locality first + isNodeLocal = connectionOutService.isLocal(nodeConnector.getNode()); + notifyGlobalInventoryShimInternalListener(nodeConnector, type, props); + } else { + notifyGlobalInventoryShimInternalListener(nodeConnector, type, props); + isNodeLocal = connectionOutService.isLocal(nodeConnector.getNode()); + } + + if (isNodeLocal) { + // notify other containers + Set containers = (nodeConnectorContainerMap.get(nodeConnector) == null) ? new HashSet() + : new HashSet(nodeConnectorContainerMap.get(nodeConnector)); + containers.add(GlobalConstants.DEFAULT.toString()); for (String container : containers) { - // no property stored in container components. - notifyInventoryShimInternalListener(container, nodeConnector, - type, null); + notifyInventoryShimInternalListener(container, nodeConnector, type, props); } - } - // Notify DiscoveryService - notifyInventoryShimExternalListener(nodeConnector, type, props); + // Notify plugin listeners (Discovery, DataPacket, OFstats etc.) + notifyInventoryShimExternalListener(nodeConnector, type, props); + + logger.debug("Connection service accepted the inventory notification for {} {}", nodeConnector, type); + } else { + logger.debug("Connection service dropped the inventory notification for {} {}", nodeConnector, type); + } } /* * Notify all internal and external listeners */ - private void notifyInventoryShimListener(Node node, UpdateType type, - Set props) { - switch (type) { - case ADDED: - // Notify only the default Inventory Service - IInventoryShimInternalListener inventoryShimDefaultListener = inventoryShimInternalListeners - .get(GlobalConstants.DEFAULT.toString()); - if (inventoryShimDefaultListener != null) { - inventoryShimDefaultListener.updateNode(node, type, props); - } - break; - case REMOVED: - // Notify all Inventory Service containers - for (IInventoryShimInternalListener inventoryShimInternalListener : inventoryShimInternalListeners - .values()) { - inventoryShimInternalListener.updateNode(node, type, null); + private void notifyInventoryShimListener(Node node, UpdateType type, Set props) { + + //establish locality before notifying + boolean isNodeLocal; + if (type == UpdateType.REMOVED){ + //if removing get the locality first + isNodeLocal = connectionOutService.isLocal(node); + notifyGlobalInventoryShimInternalListener(node, type, props); + } else { + notifyGlobalInventoryShimInternalListener(node, type, props); + isNodeLocal = connectionOutService.isLocal(node); + } + + if (isNodeLocal) { + // Now notify other containers + Set containers = (nodeContainerMap.get(node) == null) ? new HashSet() + : new HashSet(nodeContainerMap.get(node)); + containers.add(GlobalConstants.DEFAULT.toString()); + for (String container : containers) { + notifyInventoryShimInternalListener(container, node, type, props); } - break; - default: - break; + + // Notify plugin listeners (Discovery, DataPacket, OFstats etc.) + notifyInventoryShimExternalListener(node, type, props); + + logger.debug("Connection service accepted the inventory notification for {} {}", node, type); + } else { + logger.debug("Connection service dropped the inventory notification for {} {}", node, type); } + } - // Notify external listener - notifyInventoryShimExternalListener(node, type, props); + private void notifyGlobalInventoryShimInternalListener(Node node, UpdateType type, Set props) { + for (IInventoryShimInternalListener globalListener : globalInventoryShimInternalListeners) { + globalListener.updateNode(node, type, props); + logger.trace("notifyGlobalInventoryShimInternalListener {} type {}", new Object[] { node, type }); + } } - private void addNode(ISwitch sw) { - Node node; - try { - node = new Node(NodeIDType.OPENFLOW, sw.getId()); - } catch (ConstructionException e) { - logger.error("{}", e.getMessage()); - return; + private void notifyGlobalInventoryShimInternalListener(NodeConnector nodeConnector, UpdateType type, Set props) { + for (IInventoryShimInternalListener globalListener : globalInventoryShimInternalListeners) { + globalListener.updateNodeConnector(nodeConnector, type, props); + logger.trace( + "notifyGlobalInventoryShimInternalListener {} type {}", + new Object[] { nodeConnector, type }); } + } + + private void notifyInventoryShimInternalListener(String container, + Node node, UpdateType type, Set props) { + IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners + .get(container); + if (inventoryShimInternalListener != null) { + inventoryShimInternalListener.updateNode(node, type, props); + logger.trace( + "notifyInventoryShimInternalListener {} type {} for container {}", + new Object[] { node, type, container }); + } + } + private void addNode(ISwitch sw) { + Node node = NodeCreator.createOFNode(sw.getId()); UpdateType type = UpdateType.ADDED; Set props = new HashSet(); Long sid = (Long) node.getID(); - Date connectedSince = controller.getSwitches().get(sid) - .getConnectedDate(); + Date connectedSince = sw.getConnectedDate(); Long connectedSinceTime = (connectedSince == null) ? 0 : connectedSince .getTime(); props.add(new TimeStamp(connectedSinceTime, "connectedSince")); + props.add(new MacAddress(deriveMacAddress(sid))); byte tables = sw.getTables(); Tables t = new Tables(tables); @@ -390,7 +503,7 @@ public class InventoryServiceShim implements IContainerListener, props.add(c); } int act = sw.getActions(); - Actions a = new Actions(act); + SupportedFlowActions a = new SupportedFlowActions(FlowConverter.getFlowActions(act)); if (a != null) { props.add(a); } @@ -399,21 +512,25 @@ public class InventoryServiceShim implements IContainerListener, if (b != null) { props.add(b); } + + if ((nodeProps.get(node) == null) && (connectionOutService.isLocal(node))) { + // The switch is connected for the first time, flush all flows + // that may exist on this switch + sw.deleteAllFlows(); + } + nodeProps.put(node, props); // Notify all internal and external listeners notifyInventoryShimListener(node, type, props); } private void removeNode(ISwitch sw) { - Node node; - try { - node = new Node(NodeIDType.OPENFLOW, sw.getId()); - } catch (ConstructionException e) { - logger.error("{}", e.getMessage()); + Node node = NodeCreator.createOFNode(sw.getId()); + if(node == null) { return; } - + removeNodeConnectorProps(node); + nodeProps.remove(node); UpdateType type = UpdateType.REMOVED; - // Notify all internal and external listeners notifyInventoryShimListener(node, type, null); } @@ -425,4 +542,93 @@ public class InventoryServiceShim implements IContainerListener, switchAdded(sw); } } + + private void removeNodeConnectorProps(Node node) { + List ncList = new ArrayList(); + for (NodeConnector nc : nodeConnectorProps.keySet()) { + if (nc.getNode().equals(node)) { + ncList.add(nc); + } + } + for (NodeConnector nc : ncList) { + nodeConnectorProps.remove(nc); + } + } + + @Override + public void descriptionStatisticsRefreshed(Long switchId, List descriptionStats) { + Node node = NodeCreator.createOFNode(switchId); + Set properties = new HashSet(1); + OFDescriptionStatistics ofDesc = (OFDescriptionStatistics) descriptionStats.get(0); + Description desc = new Description(ofDesc.getDatapathDescription()); + properties.add(desc); + + // Notify all internal and external listeners + notifyInventoryShimListener(node, UpdateType.CHANGED, properties); + } + + private byte[] deriveMacAddress(long dpid) { + byte[] mac = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + for (short i = 0; i < 6; i++) { + mac[5 - i] = (byte) dpid; + dpid >>= 8; + } + + return mac; + } + + @Override + public void flowStatisticsRefreshed(Long switchId, List flows) { + // Nothing to do + } + + @Override + public void portStatisticsRefreshed(Long switchId, List ports) { + // Nothing to do + } + + @Override + public void tableStatisticsRefreshed(Long switchId, List tables) { + // Nothing to do + } + + @Override + public void containerCreate(String containerName) { + // Nothing to do + } + + @Override + public void containerDestroy(String containerName) { + Set removeNodeConnectorSet = new HashSet(); + Set removeNodeSet = new HashSet(); + for (Map.Entry> entry : nodeConnectorContainerMap.entrySet()) { + Set ncContainers = entry.getValue(); + if (ncContainers.contains(containerName)) { + NodeConnector nodeConnector = entry.getKey(); + removeNodeConnectorSet.add(nodeConnector); + } + } + for (Map.Entry> entry : nodeContainerMap.entrySet()) { + Set nodeContainers = entry.getValue(); + if (nodeContainers.contains(containerName)) { + Node node = entry.getKey(); + removeNodeSet.add(node); + } + } + for (NodeConnector nodeConnector : removeNodeConnectorSet) { + Set ncContainers = nodeConnectorContainerMap.get(nodeConnector); + ncContainers.remove(containerName); + if (ncContainers.isEmpty()) { + nodeConnectorContainerMap.remove(nodeConnector); + } + } + for (Node node : removeNodeSet) { + Set nodeContainers = nodeContainerMap.get(node); + nodeContainers.remove(containerName); + if (nodeContainers.isEmpty()) { + nodeContainerMap.remove(node); + } + } + } }