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