Merge "BUG3685, BUG3686"
[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.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import java.util.ArrayList;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Set;
44
45 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
46 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbTerminationPoint;
47 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
48 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.submitToDs;
49
50 public class InventoryHelper {
51
52     private static final Logger LOG = LoggerFactory.getLogger(InventoryHelper.class);
53     private static final String HEX = "0x";
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 OVSDB bridge augmentation
78      * @return String representation of the Inventory NodeId, null if it fails
79      */
80     public static String getInventoryNodeIdString(OvsdbBridgeAugmentation ovsdbBridge,
81             InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid, DataBroker dataBroker) {
82         DatapathId dpid = ovsdbBridge.getDatapathId();
83         if (dpid == null) {
84             OvsdbBridgeAugmentation bridgeData = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
85             dpid = bridgeData.getDatapathId();
86             if (dpid == null) {
87                 LOG.error("No Data Path ID for OVSDB Bridge {}", ovsdbBridge);
88                 return null;
89             }
90         }
91         Long macLong = getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
92         String nodeIdString = "openflow:" + String.valueOf(macLong);
93         if (StringUtils.countMatches(nodeIdString, ":") != 1) {
94             LOG.error("{} is not correct format for NodeId.", nodeIdString);
95             return null;
96         }
97         return nodeIdString;
98     }
99
100     /**
101      * Construct a string that can be used to create a
102      * {@link org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId}.
103      * The String is constructed by first getting the inventory Node ID string, and then
104      * adding the port number obtained by the OVSDB augmentation.
105      *
106      * @param inventoryNodeId The string representation of the Inventory NodeId
107      * @param ovsdbTp The {@link OvsdbTerminationPointAugmentation}
108      * @return String representation of the Inventory NodeConnectorId, null if it fails
109      */
110     public static String getInventoryNodeConnectorIdString(String inventoryNodeId,
111             OvsdbTerminationPointAugmentation ovsdbTp, InstanceIdentifier<OvsdbTerminationPointAugmentation> tpIid,
112             DataBroker dataBroker) {
113         Long ofport = null;
114         if (ovsdbTp.getOfport() != null && ovsdbTp.getOfport() > MAX_OF_PORT) {
115             LOG.debug("Invalid OpenFlow port {} for {}", ovsdbTp.getOfport(), ovsdbTp);
116             return null;
117         }
118         if (ovsdbTp.getOfport() == null) {
119             OvsdbTerminationPointAugmentation readOvsdbTp = getOvsdbTerminationPoint(tpIid, dataBroker);
120             if (readOvsdbTp == null || readOvsdbTp.getOfport() == null || readOvsdbTp.getOfport() > MAX_OF_PORT) {
121                 LOG.debug("Couldn't get OpenFlow port for {}", ovsdbTp);
122                 return null;
123             }
124             ofport = readOvsdbTp.getOfport();
125         } else {
126             ofport = ovsdbTp.getOfport();
127         }
128         String nodeConnectorIdString = inventoryNodeId + ":" + String.valueOf(ofport);
129
130         if (StringUtils.countMatches(nodeConnectorIdString, ":") != 2) {
131             LOG.error("{} is not correct format for NodeConnectorId.", nodeConnectorIdString);
132             return null;
133         }
134         return nodeConnectorIdString;
135     }
136
137     /**
138      * Read the {@link OfOverlayNodeConfig} augmentation from the
139      * Inventory Node, and verify that the tunnel types we need
140      * are present
141      *
142      * @return true if tunnel types are present, false otherwise
143      */
144     public static boolean checkOfOverlayConfig(String nodeIdString, List<AbstractTunnelType> requiredTunnelTypes,
145             DataBroker dataBroker) {
146         OfOverlayNodeConfig config = getOfOverlayConfig(nodeIdString, dataBroker);
147         if (config == null || config.getTunnel() == null) {
148             LOG.debug("No OfOverlay config for {}", nodeIdString);
149             return false;
150         }
151
152         /*
153          * See if the OfOverlayNodeConfig has the
154          * tunnel type information.
155          */
156         for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
157             boolean tunnelPresent = false;
158             for (Tunnel tunnel : config.getTunnel()) {
159                 if (tunnelType.getTunnelType().equals(tunnel.getTunnelType())) {
160                     tunnelPresent = true;
161                     break;
162                 }
163             }
164             if (tunnelPresent == false) {
165                 return false;
166             }
167         }
168         return true;
169     }
170
171     public static void addOfOverlayExternalPort(String nodeIdString, NodeConnectorId ncId, DataBroker dataBroker) {
172         InstanceIdentifier<ExternalInterfaces> nodeExternalInterfacesIid = InstanceIdentifier.builder(Nodes.class)
173             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
174             .augmentation(OfOverlayNodeConfig.class)
175             .child(ExternalInterfaces.class, new ExternalInterfacesKey(ncId))
176             .build();
177
178         ExternalInterfaces externalInterfaces = new ExternalInterfacesBuilder().setKey(new ExternalInterfacesKey(ncId))
179             .setNodeConnectorId(ncId)
180             .build();
181         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
182         transaction.put(LogicalDatastoreType.CONFIGURATION, nodeExternalInterfacesIid, externalInterfaces, true);
183         submitToDs(transaction);
184         LOG.trace("Added external interface node connector {} to node {}", ncId.getValue(), nodeIdString);
185     }
186
187     public static OfOverlayNodeConfig getOfOverlayConfig(String nodeIdString, DataBroker dataBroker) {
188         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(Nodes.class)
189             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
190             .augmentation(OfOverlayNodeConfig.class)
191             .build();
192
193         ReadWriteTransaction transaction = dataBroker.newReadWriteTransaction();
194         Optional<OfOverlayNodeConfig> overlayConfig = readFromDs(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid,
195                 transaction);
196         if (overlayConfig.isPresent()) {
197             return overlayConfig.get();
198         }
199         return null;
200     }
201
202     private static boolean addOfOverlayAugmentation(OfOverlayNodeConfig config, String nodeIdString, DataBroker dataBroker) {
203         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(Nodes.class)
204             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
205             .augmentation(OfOverlayNodeConfig.class)
206             .build();
207
208         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
209         transaction.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, config, true);
210         return submitToDs(transaction);
211     }
212
213     /**
214      * Update the {@link OfOverlayConfig} of an Inventory Node
215      * using the new tunnel state.
216      *
217      * @param ip
218      * @param nodeIdString
219      * @param nodeConnectorIdString
220      * @param tunnelType
221      * @param dataBroker
222      */
223     public static void updateOfOverlayConfig(IpAddress ip, String nodeIdString, String nodeConnectorIdString,
224             AbstractTunnelType tunnelType, DataBroker dataBroker) {
225
226         if ((ip == null) || (nodeIdString == null) || (nodeConnectorIdString == null)) {
227             LOG.debug("Can't update OfOverlay: requisite information not present");
228             return;
229         }
230         NodeConnectorId nodeConnectorId = new NodeConnectorId(nodeConnectorIdString);
231
232
233         // Pull existing augmentation
234         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
235
236         // If it exists, use it in new augmentation constructor, else new augmentation
237         OfOverlayNodeConfigBuilder ofOverlayNodeConfigBuilder;
238         Set<Tunnel> existingTunnels = new HashSet<Tunnel>();
239         if (ofConfig != null) {
240             ofOverlayNodeConfigBuilder = new OfOverlayNodeConfigBuilder(ofConfig);
241             if(ofConfig.getTunnel() != null) {
242                 existingTunnels.addAll(ofConfig.getTunnel());
243             }
244         } else {
245             ofOverlayNodeConfigBuilder = new OfOverlayNodeConfigBuilder();
246         }
247
248         // Determine if this is an update to existing tunnel of this type or a new tunnel
249         boolean tunnelsUpdated = false;
250         TunnelBuilder tunnelBuilder = new TunnelBuilder();
251
252         boolean tunnelFound = false;
253         for (Tunnel currentTun : existingTunnels) {
254             if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
255                 // tunnel update
256                 tunnelBuilder.setIp(ip);
257                 tunnelBuilder.setPort(tunnelType.getPortNumber());
258                 tunnelBuilder.setNodeConnectorId(nodeConnectorId);
259                 tunnelFound = true;
260                 tunnelsUpdated = true;
261                 break;
262             }
263         }
264         // new tunnel
265         if (tunnelFound == false) {
266             tunnelBuilder.setIp(ip);
267             tunnelBuilder.setPort(tunnelType.getPortNumber());
268             tunnelBuilder.setNodeConnectorId(nodeConnectorId);
269             tunnelBuilder.setTunnelType(tunnelType.getTunnelType());
270             tunnelsUpdated = true;
271         }
272
273         // Nothing was updated, nothing to see here, move along...
274         if (tunnelsUpdated == false) {
275             return;
276         }
277
278         existingTunnels.add(tunnelBuilder.build());
279
280         // Update the OfOverlayNodeConfig with the new tunnel information
281         OfOverlayNodeConfig newConfig=ofOverlayNodeConfigBuilder.setTunnel(new ArrayList<Tunnel>(existingTunnels)).build();
282         if (addOfOverlayAugmentation(newConfig, 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             OfOverlayNodeConfigBuilder ofOverlayBuilder;
315             if (ofConfig == null) {
316                 ofOverlayBuilder = new OfOverlayNodeConfigBuilder();
317             } else {
318                 ofOverlayBuilder = new OfOverlayNodeConfigBuilder(ofConfig);
319             }
320             ofOverlayBuilder.setTunnel(existingTunnels);
321             addOfOverlayAugmentation(ofOverlayBuilder.build(), nodeIdString, dataBroker);
322         }
323     }
324 }