Merge "Tests for neutron-ovsdb"
[groupbasedpolicy.git] / neutron-ovsdb / src / main / java / org / opendaylight / groupbasedpolicy / neutron / ovsdb / OvsdbNodeListener.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.groupbasedpolicy.neutron.ovsdb;
9
10 import javax.annotation.Nonnull;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Objects;
16 import java.util.Set;
17
18 import com.google.common.base.Function;
19 import com.google.common.base.Strings;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.Lists;
22 import com.google.common.util.concurrent.FutureCallback;
23 import com.google.common.util.concurrent.Futures;
24 import org.opendaylight.controller.config.yang.config.neutron_ovsdb.impl.IntegrationBridgeSetting;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper;
31 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronOvsdbIidFactory;
32 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
33 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeSystem;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentationBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeName;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeProtocolOpenflow13;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntryBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntryBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagerEntry;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigsKey;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
60 import org.opendaylight.yangtools.yang.binding.DataObject;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 public class OvsdbNodeListener extends DataTreeChangeHandler<Node> {
66
67     static final String BRIDGE_SEPARATOR = "/bridge/";
68     static final String NEUTRON_PROVIDER_MAPPINGS_KEY = "provider_mappings";
69     private static final Logger LOG = LoggerFactory.getLogger(OvsdbNodeListener.class);
70     private static final String OF_SEPARATOR = ":";
71     private static final String OF_INVENTORY_PREFIX = "openflow";
72     private static IntegrationBridgeSetting intBrSettings;
73
74     private final Map<OvsdbBridgeRef, String> providerPortNameByBridgeRef = new HashMap<>();
75     private final Map<InstanceIdentifier<Node>, NeutronBridgeWithExtPort> bridgeByNodeIid = new HashMap<>();
76
77     public OvsdbNodeListener(DataBroker dataProvider, IntegrationBridgeSetting brSettings) {
78         super(dataProvider);
79         intBrSettings = brSettings;
80         this.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
81                 InstanceIdentifier.create(NetworkTopology.class)
82                     .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
83                     .child(Node.class)));
84     }
85
86     @Override
87     protected void onWrite(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
88         Node node = rootNode.getDataAfter();
89         OvsdbNodeAugmentation ovsdbNode = node.getAugmentation(OvsdbNodeAugmentation.class);
90         if (ovsdbNode != null) {
91             LOG.trace("OVSDB node created: {} \n {}", rootIdentifier, node);
92             DataObjectModification<OpenvswitchOtherConfigs> ovsOtherConfigModification = getProviderMappingsModification(rootNode);
93             boolean integrationBridgePresent = false;
94             if (isProviderPortNameChanged(ovsOtherConfigModification) && ovsdbNode.getManagedNodeEntry() != null) {
95                 String newProviderPortName = getProviderPortName(ovsOtherConfigModification.getDataAfter());
96                 LOG.debug("provider_mappings created {} on node {}", newProviderPortName, node.getNodeId().getValue());
97                 for (ManagedNodeEntry mngdNodeEntry : ovsdbNode.getManagedNodeEntry()) {
98                     OvsdbBridgeRef bridgeRef = mngdNodeEntry.getBridgeRef();
99                     providerPortNameByBridgeRef.put(bridgeRef, newProviderPortName);
100                     LOG.trace("Added Provider port name {} by OVSDB bridge ref {}", newProviderPortName,
101                             mngdNodeEntry.getBridgeRef());
102                     NodeKey managedNodeKey = bridgeRef.getValue().firstKeyOf(Node.class);
103                     if (intBrSettings != null && managedNodeKey.getNodeId().getValue().equals(intBrSettings.getName())) {
104                         integrationBridgePresent = true;
105                     }
106                 }
107             }
108             if (intBrSettings != null && !integrationBridgePresent) {
109                 final Node bridge = createBridge(rootIdentifier,
110                         managerToControllerEntries(ovsdbNode.getManagerEntry()), intBrSettings.getName());
111                 InstanceIdentifier<Node> bridgeNodeIid = NeutronOvsdbIidFactory.nodeIid(
112                         rootIdentifier.firstKeyOf(Topology.class).getTopologyId(), bridge.getNodeId());
113                 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
114                 wTx.merge(LogicalDatastoreType.CONFIGURATION, bridgeNodeIid, bridge, true);
115                 Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
116
117                     @Override
118                     public void onSuccess(Void result) {
119                         LOG.info("Bridge {} written to datastore." + bridge.getNodeId().getValue());
120                     }
121
122                     @Override
123                     public void onFailure(Throwable t) {
124                         LOG.error("Failed to write bridge {}. Message: {}" + bridge.getNodeId().getValue(),
125                                 t.getMessage());
126                     }
127                 });
128             }
129         }
130         OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
131         if (ovsdbBridge != null) {
132             LOG.trace("OVSDB bridge created: {} \n {}", rootIdentifier, node);
133             Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
134                     getOvsdbTpModifications(rootNode);
135             NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
136             if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
137                 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
138                 bridge.ofNodeId = ofNodeId;
139                 LOG.trace("OF node {} representing OVSDB bridge {}", ofNodeId.getValue(), node.getNodeId().getValue());
140             }
141             for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
142                 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
143                 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
144                     LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
145                             ovsdbBridge.getBridgeName().getValue());
146                     continue;
147                 }
148                 String portName = newOvsdbTp.getName();
149                 Long ofport = newOvsdbTp.getOfport();
150                 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
151                     NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
152                     bridge.ofportByName.put(ofport, portName);
153                     LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
154                     // port name is same as provider port name so the termination point represents
155                     // external port
156                     if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
157                         NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
158                         bridge.externalIfaces.add(ofNcId);
159                         InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
160                         LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
161                                 bridge.ofNodeId);
162                         traceBridge(rootIdentifier);
163                     }
164                 }
165             }
166         }
167     }
168
169     @Override
170     protected void onDelete(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
171         LOG.trace("Not implemented - OVSDB element deleted: {} \n {}", rootIdentifier, rootNode.getDataBefore());
172     }
173
174     @Override
175     protected void onSubtreeModified(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
176         Node node = rootNode.getDataAfter();
177         OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
178         if (ovsdbBridge != null) {
179             LOG.trace("OVSDB bridge updated: {} \n before {} \n after {}", rootIdentifier, rootNode.getDataBefore(),
180                     rootNode.getDataAfter());
181             Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
182                     getOvsdbTpModifications(rootNode);
183             NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
184             if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
185                 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
186                 if (bridge.ofNodeId != null && !bridge.ofNodeId.equals(ofNodeId)) {
187                     LOG.debug("OVSDB bridge {} has changed datapath-id. \n  Old: {} \n  New: {}",
188                             node.getNodeId().getValue(), bridge.ofNodeId.getValue(), ofNodeId.getValue());
189                     bridge.ofNodeId = ofNodeId;
190                 }
191             }
192             for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
193                 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
194
195                 if (newOvsdbTp == null) {
196                     LOG.trace("Termination Point is null. Not processing");
197                     continue;
198                 }
199
200                 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
201                     LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
202                             ovsdbBridge.getBridgeName().getValue());
203                     continue;
204                 }
205                 String portName = newOvsdbTp.getName();
206                 Long ofport = newOvsdbTp.getOfport();
207                 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
208                     NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
209                     bridge.ofportByName.put(ofport, portName);
210                     LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
211                     // port name is same as provider port name so the termination point represents
212                     // external port
213                     if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
214                         NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
215                         bridge.externalIfaces.add(ofNcId);
216                         InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
217                         LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
218                                 bridge.ofNodeId);
219                         traceBridge(rootIdentifier);
220                     }
221                 }
222             }
223         }
224     }
225
226     private NeutronBridgeWithExtPort getBridge(InstanceIdentifier<Node> nodeIid) {
227         NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(nodeIid);
228         if (bridge == null) {
229             bridge = new NeutronBridgeWithExtPort();
230             bridgeByNodeIid.put(nodeIid, bridge);
231         }
232         return bridge;
233     }
234
235     @SuppressWarnings("unchecked")
236     private static Set<DataObjectModification<OvsdbTerminationPointAugmentation>> getOvsdbTpModifications(
237             DataObjectModification<Node> rootNode) {
238         Set<DataObjectModification<OvsdbTerminationPointAugmentation>> modifications = new HashSet<>();
239         for (DataObjectModification<? extends DataObject> modifiedChild : rootNode.getModifiedChildren()) {
240             if (TerminationPoint.class.isAssignableFrom(modifiedChild.getDataType())) {
241                 DataObjectModification<OvsdbTerminationPointAugmentation> modifiedAugmentation =
242                         ((DataObjectModification<TerminationPoint>) modifiedChild)
243                             .getModifiedAugmentation(OvsdbTerminationPointAugmentation.class);
244                 if (modifiedAugmentation != null) {
245                     modifications.add(modifiedAugmentation);
246                 }
247             }
248         }
249         return modifications;
250     }
251
252     private static boolean isOfportOrNameChanged(
253             DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification) {
254         if (ovsdbTpModification == null) {
255             return false;
256         }
257         OvsdbTerminationPointAugmentation oldTp = ovsdbTpModification.getDataBefore();
258         OvsdbTerminationPointAugmentation newTp = ovsdbTpModification.getDataAfter();
259         if (oldTp != null && newTp != null) {
260             if (oldTp.getOfport() != null && newTp.getOfport() != null && !Objects.equals(oldTp.getOfport(),
261                 newTp.getOfport())) {
262                 return true;
263             }
264             if (!(Strings.nullToEmpty(oldTp.getName())).equals(Strings.nullToEmpty(newTp.getName()))) {
265                 return true;
266             }
267         }
268         if (isOfportOrNameNotNull(oldTp)) {
269             return true;
270         }
271         if (isOfportOrNameNotNull(newTp)) {
272             return true;
273         }
274         return false;
275     }
276
277     private static boolean isOfportOrNameNotNull(OvsdbTerminationPointAugmentation tp) {
278         if (tp != null) {
279             if (tp.getOfport() != null) {
280                 return true;
281             }
282             if (tp.getName() != null) {
283                 return true;
284             }
285         }
286         return false;
287     }
288
289     private static DataObjectModification<OpenvswitchOtherConfigs> getProviderMappingsModification(
290             DataObjectModification<Node> rootNode) {
291         DataObjectModification<OvsdbNodeAugmentation> modifiedOvsdbNode =
292                 rootNode.getModifiedAugmentation(OvsdbNodeAugmentation.class);
293         if (modifiedOvsdbNode == null) {
294             return null;
295         }
296         return modifiedOvsdbNode.getModifiedChildListItem(OpenvswitchOtherConfigs.class,
297                 new OpenvswitchOtherConfigsKey(NEUTRON_PROVIDER_MAPPINGS_KEY));
298     }
299
300     private static boolean isProviderPortNameChanged(DataObjectModification<OpenvswitchOtherConfigs> ovsConfig) {
301         if (ovsConfig == null) {
302             return false;
303         }
304         OpenvswitchOtherConfigs oldConfig = ovsConfig.getDataBefore();
305         OpenvswitchOtherConfigs newConfig = ovsConfig.getDataAfter();
306         if (oldConfig != null && newConfig != null) {
307             if (!(Strings.nullToEmpty(oldConfig.getOtherConfigValue())
308                 .equals(Strings.nullToEmpty(newConfig.getOtherConfigValue())))) {
309                 return true;
310             }
311         } else if (oldConfig != null && !Strings.isNullOrEmpty(oldConfig.getOtherConfigValue())) {
312             return true;
313         } else if (newConfig != null && !Strings.isNullOrEmpty(newConfig.getOtherConfigValue())) {
314             return true;
315         }
316         return false;
317     }
318
319     private static @Nonnull String getProviderPortName(OpenvswitchOtherConfigs config) {
320         if (NEUTRON_PROVIDER_MAPPINGS_KEY.equals(config.getOtherConfigKey()) && config.getOtherConfigValue() != null) {
321             String otherConfig = config.getOtherConfigValue();
322             String[] elements = otherConfig.split(":");
323             if (elements.length == 2) {
324                 return elements[1];
325             }
326         }
327         return "";
328     }
329
330     /**
331      * Extracts IP address from URI
332      *
333      * @param uri in format protocol:ip:port
334      * @return IPv4 or IPv6 address as {@link String}.
335      */
336     private static @Nonnull String getIpAddrFromUri(Uri uri) {
337         String otherConfig = uri.getValue();
338         String[] elements = otherConfig.split(":");
339         // IPv6 expression also contains colons
340         if (elements.length < 3) {
341             return "";
342         }
343         StringBuilder sb = new StringBuilder();
344         // first (protocol) and last (port) elements are filtered
345         for (int i = 1; i < elements.length - 1; i++) {
346             sb.append(elements[i]);
347         }
348         return sb.toString();
349     }
350
351     private List<ControllerEntry> managerToControllerEntries(List<ManagerEntry> managerEntries) {
352         return Lists.transform(managerEntries, new Function<ManagerEntry, ControllerEntry>() {
353
354             @Override
355             public ControllerEntry apply(ManagerEntry managerEntry) {
356                 String ipAddr = getIpAddrFromUri(managerEntry.getTarget());
357                 Uri uri = new Uri(intBrSettings.getOpenflowProtocol() + OF_SEPARATOR + ipAddr + OF_SEPARATOR
358                         + intBrSettings.getOpenflowPort());
359                 return new ControllerEntryBuilder().setTarget(new Uri(uri)).build();
360             }
361         });
362     }
363
364     private Node createBridge(InstanceIdentifier<Node> managedByIid, List<ControllerEntry> controllerEntries,
365             String bridgeName) {
366         OvsdbBridgeAugmentation br = new OvsdbBridgeAugmentationBuilder()
367             .setBridgeName(new OvsdbBridgeName(bridgeName))
368             .setManagedBy(new OvsdbNodeRef(managedByIid))
369             .setControllerEntry(controllerEntries)
370             .setDatapathType(DatapathTypeSystem.class)
371             .setProtocolEntry(
372                     ImmutableList.of(new ProtocolEntryBuilder().setProtocol(
373                             OvsdbBridgeProtocolOpenflow13.class).build()))
374             .build();
375         NodeKey managerNodeKey = managedByIid.firstKeyOf(Node.class);
376         return new NodeBuilder().setNodeId(
377                 new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(
378                         managerNodeKey.getNodeId().getValue() + BRIDGE_SEPARATOR + bridgeName))
379             .addAugmentation(OvsdbBridgeAugmentation.class, br)
380             .build();
381     }
382
383     private static NodeId buildOfNodeId(OvsdbBridgeAugmentation ovsdbBridge) {
384         if (ovsdbBridge.getDatapathId() == null) {
385             return null;
386         }
387         Long macLong = InventoryHelper.getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
388         return new NodeId(OF_INVENTORY_PREFIX + OF_SEPARATOR + String.valueOf(macLong));
389     }
390
391     private static NodeConnectorId buildOfNodeConnectorId(OvsdbTerminationPointAugmentation terminationPoint,
392             NodeId nodeId) {
393         if (terminationPoint.getOfport() == null) {
394             return null;
395         }
396         return new NodeConnectorId(nodeId.getValue() + OF_SEPARATOR + String.valueOf(terminationPoint.getOfport()));
397     }
398
399     private void traceBridge(InstanceIdentifier<Node> identifier) {
400         if (LOG.isTraceEnabled()) {
401             NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(identifier);
402             if (bridge == null) {
403                 LOG.trace("Bridge does not exist: {}", identifier);
404                 return;
405             }
406             String providerPortName = providerPortNameByBridgeRef.get(new OvsdbBridgeRef(identifier));
407             LOG.trace("State of bridge:\n  ID: {} \n  providerPortName: {} \n  {}", identifier, providerPortName,
408                     bridge);
409         }
410     }
411
412     private class NeutronBridgeWithExtPort {
413
414         NodeId ofNodeId;
415         Set<NodeConnectorId> externalIfaces = new HashSet<>();
416         Map<Long, String> ofportByName = new HashMap<>();
417
418         @Override
419         public String toString() {
420             return "NeutronBridgeWithExtPort:\n  ofNodeId=" + ofNodeId + "\n  externalIfaces=" + externalIfaces
421                     + ",\n  ofportByName=" + ofportByName;
422         }
423     }
424
425 }