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