Bug 4098 - ofoverlay-renderer failure in jdk8
[groupbasedpolicy.git] / neutron-ovsdb / src / main / java / org / opendaylight / groupbasedpolicy / neutron / ovsdb / util / InventoryHelper.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.util;
9
10 import com.google.common.base.Optional;
11 import java.util.HashSet;
12 import java.util.Set;
13 import org.apache.commons.lang3.StringUtils;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
16 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.AbstractTunnelType;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfigBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesKey;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.Tunnel;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import java.util.ArrayList;
40 import java.util.List;
41
42 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
43 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbTerminationPoint;
44 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
45 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.submitToDs;
46
47 public class InventoryHelper {
48
49     private static final Logger LOG = LoggerFactory.getLogger(InventoryHelper.class);
50     private static final String HEX = "0x";
51
52     /**
53      * Convert an OpenFlow Datapath ID to a Long
54      *
55      * @param dpid The OpenFlow Datapath ID
56      * @return The Long representation of the DPID
57      */
58     public static Long getLongFromDpid(String dpid) {
59         String[] addressInBytes = dpid.split(":");
60         Long address = (Long.decode(HEX + addressInBytes[2]) << 40) | (Long.decode(HEX + addressInBytes[3]) << 32)
61                 | (Long.decode(HEX + addressInBytes[4]) << 24) | (Long.decode(HEX + addressInBytes[5]) << 16)
62                 | (Long.decode(HEX + addressInBytes[6]) << 8) | (Long.decode(HEX + addressInBytes[7]));
63         return address;
64     }
65
66     private static final Long MAX_OF_PORT = 65534L;
67
68     /**
69      * Construct a String that can be used to create a {@link NodeId}.
70      * The String is constructed by getting the Datapath ID from the OVSDB bridge
71      * augmentation, converting that to a Long, and prepending it with the
72      * "openflow:" prefix.
73      *
74      * @param ovsdbBridge The {@link OvsdbBridgeAugmentation}
75      * @param ovsdbTpIid the {@link OvsdbTerminationPointAugmentation}
76      * @param dataBroker the {@link DataBroker}
77      * @return String representation of the Inventory NodeId, null if it fails
78      */
79     public static String getInventoryNodeIdString(OvsdbBridgeAugmentation ovsdbBridge,
80             InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid, DataBroker dataBroker) {
81         DatapathId dpid = ovsdbBridge.getDatapathId();
82         if (dpid == null) {
83             OvsdbBridgeAugmentation bridgeData = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
84             dpid = bridgeData.getDatapathId();
85             if (dpid == null) {
86                 LOG.error("No Data Path ID for OVSDB Bridge {}", ovsdbBridge);
87                 return null;
88             }
89         }
90         Long macLong = getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
91         String nodeIdString = "openflow:" + String.valueOf(macLong);
92         if (StringUtils.countMatches(nodeIdString, ":") != 1) {
93             LOG.error("{} is not correct format for NodeId.", nodeIdString);
94             return null;
95         }
96         return nodeIdString;
97     }
98
99     /**
100      * Construct a string that can be used to create a
101      * {@link org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId}.
102      * The String is constructed by first getting the inventory Node ID string, and then
103      * adding the port number obtained by the OVSDB augmentation.
104      *
105      * @param inventoryNodeId The string representation of the Inventory NodeId
106      * @param ovsdbTp The {@link OvsdbTerminationPointAugmentation}
107      * @param tpIid the InstanceIdentifier for OvsdbTerminationPointAugmentation
108      * @param dataBroker the {@link DataBroker}
109      * @return String representation of the Inventory NodeConnectorId, null if it fails
110      */
111     public static String getInventoryNodeConnectorIdString(String inventoryNodeId,
112             OvsdbTerminationPointAugmentation ovsdbTp, InstanceIdentifier<OvsdbTerminationPointAugmentation> tpIid,
113             DataBroker dataBroker) {
114         Long ofport = null;
115         if (ovsdbTp.getOfport() != null && ovsdbTp.getOfport() > MAX_OF_PORT) {
116             LOG.debug("Invalid OpenFlow port {} for {}", ovsdbTp.getOfport(), ovsdbTp);
117             return null;
118         }
119         if (ovsdbTp.getOfport() == null) {
120             OvsdbTerminationPointAugmentation readOvsdbTp = getOvsdbTerminationPoint(tpIid, dataBroker);
121             if (readOvsdbTp == null || readOvsdbTp.getOfport() == null || readOvsdbTp.getOfport() > MAX_OF_PORT) {
122                 LOG.debug("Couldn't get OpenFlow port for {}", ovsdbTp);
123                 return null;
124             }
125             ofport = readOvsdbTp.getOfport();
126         } else {
127             ofport = ovsdbTp.getOfport();
128         }
129         String nodeConnectorIdString = inventoryNodeId + ":" + String.valueOf(ofport);
130
131         if (StringUtils.countMatches(nodeConnectorIdString, ":") != 2) {
132             LOG.error("{} is not correct format for NodeConnectorId.", nodeConnectorIdString);
133             return null;
134         }
135         return nodeConnectorIdString;
136     }
137
138     /**
139      * Read the {@link OfOverlayNodeConfig} augmentation from the
140      * Inventory Node, and verify that the tunnel types we need
141      * are present
142      *
143      * @param nodeIdString The inventory node id string
144      * @param requiredTunnelTypes the list of tunnel types
145      * @param dataBroker the {@link DataBroker}
146      * @return true if tunnel types are present, false otherwise
147      */
148     public static boolean checkOfOverlayConfig(String nodeIdString, List<AbstractTunnelType> requiredTunnelTypes,
149             DataBroker dataBroker) {
150         OfOverlayNodeConfig config = getOfOverlayConfig(nodeIdString, dataBroker);
151         if (config == null || config.getTunnel() == null) {
152             LOG.debug("No OfOverlay config for {}", nodeIdString);
153             return false;
154         }
155
156         /*
157          * See if the OfOverlayNodeConfig has the
158          * tunnel type information.
159          */
160         for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
161             boolean tunnelPresent = false;
162             for (Tunnel tunnel : config.getTunnel()) {
163                 if (tunnelType.getTunnelType().equals(tunnel.getTunnelType())) {
164                     tunnelPresent = true;
165                     break;
166                 }
167             }
168             if (tunnelPresent == false) {
169                 return false;
170             }
171         }
172         return true;
173     }
174
175     public static InstanceIdentifier<ExternalInterfaces> addOfOverlayExternalPort(NodeId nodeId, NodeConnectorId ncId, DataBroker dataBroker) {
176         InstanceIdentifier<ExternalInterfaces> nodeExternalInterfacesIid = InstanceIdentifier.builder(Nodes.class)
177             .child(Node.class, new NodeKey(nodeId))
178             .augmentation(OfOverlayNodeConfig.class)
179             .child(ExternalInterfaces.class, new ExternalInterfacesKey(ncId))
180             .build();
181
182         ExternalInterfaces externalInterfaces = new ExternalInterfacesBuilder().setKey(new ExternalInterfacesKey(ncId))
183             .setNodeConnectorId(ncId)
184             .build();
185         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
186         transaction.put(LogicalDatastoreType.CONFIGURATION, nodeExternalInterfacesIid, externalInterfaces, true);
187         submitToDs(transaction);
188         LOG.trace("Added external interface node connector {} to node {}", ncId.getValue(), nodeId.getValue());
189         return nodeExternalInterfacesIid;
190     }
191
192     public static OfOverlayNodeConfig getOfOverlayConfig(String nodeIdString, DataBroker dataBroker) {
193         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(Nodes.class)
194             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
195             .augmentation(OfOverlayNodeConfig.class)
196             .build();
197
198         ReadWriteTransaction transaction = dataBroker.newReadWriteTransaction();
199         Optional<OfOverlayNodeConfig> overlayConfig = readFromDs(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid,
200                 transaction);
201         if (overlayConfig.isPresent()) {
202             return overlayConfig.get();
203         }
204         return null;
205     }
206
207     /**
208      * Update the {@link OfOverlayNodeConfig} of an Inventory Node
209      * using the new tunnel state.
210      *
211      * @param ip the ipaddress
212      * @param nodeIdString the string representation of the inventory NodeId
213      * @param nodeConnectorIdString the string representation of the inventory NodeConnectorId
214      * @param tunnelType the tunnel type
215      * @param dataBroker the {@link DataBroker}
216      */
217     public static void updateOfOverlayConfig(IpAddress ip, String nodeIdString, String nodeConnectorIdString,
218             AbstractTunnelType tunnelType, DataBroker dataBroker) {
219
220         if ((ip == null) || (nodeIdString == null) || (nodeConnectorIdString == null)) {
221             LOG.debug("Can't update OfOverlay: requisite information not present");
222             return;
223         }
224         NodeConnectorId nodeConnectorId = new NodeConnectorId(nodeConnectorIdString);
225
226
227         // Pull existing augmentation
228         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
229
230         // If it exists, use it in new augmentation constructor, else new augmentation
231         OfOverlayNodeConfigBuilder ofOverlayNodeConfigBuilder;
232         Set<Tunnel> existingTunnels = new HashSet<Tunnel>();
233         if (ofConfig != null) {
234             ofOverlayNodeConfigBuilder = new OfOverlayNodeConfigBuilder(ofConfig);
235             if(ofConfig.getTunnel() != null) {
236                 existingTunnels.addAll(ofConfig.getTunnel());
237             }
238         } else {
239             ofOverlayNodeConfigBuilder = new OfOverlayNodeConfigBuilder();
240         }
241
242         // Determine if this is an update to existing tunnel of this type or a new tunnel
243         boolean tunnelsUpdated = false;
244         TunnelBuilder tunnelBuilder = new TunnelBuilder();
245
246         boolean tunnelFound = false;
247         for (Tunnel currentTun : existingTunnels) {
248             if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
249                 // tunnel update
250                 tunnelBuilder.setIp(ip);
251                 tunnelBuilder.setPort(tunnelType.getPortNumber());
252                 tunnelBuilder.setNodeConnectorId(nodeConnectorId);
253                 tunnelBuilder.setTunnelType(tunnelType.getTunnelType());
254                 tunnelFound = true;
255                 tunnelsUpdated = true;
256                 break;
257             }
258         }
259         // new tunnel
260         if (tunnelFound == false) {
261             tunnelBuilder.setIp(ip);
262             tunnelBuilder.setPort(tunnelType.getPortNumber());
263             tunnelBuilder.setNodeConnectorId(nodeConnectorId);
264             tunnelBuilder.setTunnelType(tunnelType.getTunnelType());
265             tunnelsUpdated = true;
266         }
267
268         // Nothing was updated, nothing to see here, move along...
269         if (tunnelsUpdated == false) {
270             return;
271         }
272
273         existingTunnels.add(tunnelBuilder.build());
274
275         // Update the OfOverlayNodeConfig with the new tunnel information
276         if (!existingTunnels.isEmpty()) {
277             ofOverlayNodeConfigBuilder.setTunnel(new ArrayList<Tunnel>(existingTunnels));
278         }
279         OfOverlayNodeConfig newConfig = ofOverlayNodeConfigBuilder.build();
280         if (addOfOverlayConfig(newConfig, new NodeId(nodeIdString), dataBroker)) {
281             LOG.trace("updateOfOverlayConfig - Added Tunnel: {} to Node: {} at NodeConnector: {}",tunnelBuilder.build(), nodeIdString, nodeConnectorIdString);
282             return;
283         } else {
284             LOG.error("updateOfOverlayConfig - could not write OfOverlayNodeConfig: {} to datastore.", newConfig);
285         }
286     }
287
288     public static void removeTunnelsOfOverlayConfig(String nodeIdString,
289                                                     List<AbstractTunnelType> tunnels,
290                                                     DataBroker dataBroker) {
291
292         if (nodeIdString == null) {
293             LOG.debug("Can't update OfOverlay: requisite information not present");
294             return;
295         }
296         List<Tunnel> existingTunnels = new ArrayList<>();
297         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
298         if (ofConfig != null) {
299             existingTunnels = ofConfig.getTunnel();
300         }
301         Set<Tunnel> tunnelsToRemove = new HashSet<>();
302         for (AbstractTunnelType tunnelType : tunnels) {
303             for (Tunnel currentTun : existingTunnels) {
304                 if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
305                     tunnelsToRemove.add(currentTun);
306                 }
307             }
308         }
309
310         // runs only if some tunnels were really removed
311         if (existingTunnels.removeAll(tunnelsToRemove)) {
312             ReadWriteTransaction wTx = dataBroker.newReadWriteTransaction();
313             for (Tunnel tunnel : tunnelsToRemove) {
314                 InstanceIdentifier<Tunnel> tunnelIid = InstanceIdentifier.builder(Nodes.class)
315                     .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
316                     .augmentation(OfOverlayNodeConfig.class)
317                     .child(Tunnel.class, tunnel.getKey())
318                     .build();
319                 wTx.delete(LogicalDatastoreType.CONFIGURATION, tunnelIid);
320                 LOG.trace("Removing tunnel: {} from node {}",tunnel, nodeIdString);
321             }
322             submitToDs(wTx);
323         }
324     }
325
326     private static boolean addOfOverlayConfig(OfOverlayNodeConfig newConfig, NodeId nodeId, DataBroker dataBroker) {
327         ReadWriteTransaction wTx = dataBroker.newReadWriteTransaction();
328         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(Nodes.class)
329             .child(Node.class, new NodeKey(nodeId))
330             .augmentation(OfOverlayNodeConfig.class)
331             .build();
332         wTx.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, newConfig, true);
333         LOG.trace("Adding tunnel: {} to node {}", newConfig, nodeId.getValue());
334         return submitToDs(wTx);
335     }
336
337     private static boolean addTunnelsOfOverlayConfig(List<Tunnel> tunnels, NodeId nodeId, DataBroker dataBroker) {
338         ReadWriteTransaction wTx = dataBroker.newReadWriteTransaction();
339         for (Tunnel tunnel : tunnels) {
340             InstanceIdentifier<Tunnel> tunnelIid = InstanceIdentifier.builder(Nodes.class)
341                 .child(Node.class, new NodeKey(nodeId))
342                 .augmentation(OfOverlayNodeConfig.class)
343                 .child(Tunnel.class, tunnel.getKey())
344                 .build();
345             wTx.put(LogicalDatastoreType.CONFIGURATION, tunnelIid, tunnel, true);
346             LOG.trace("Adding tunnel: {} to node {}",tunnel, nodeId.getValue());
347         }
348         return submitToDs(wTx);
349     }
350 }