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