Merge "Bug 3268: OVSDB TP listener not processing deletes"
[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 org.apache.commons.lang3.StringUtils;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
14 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.AbstractTunnelType;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfigBuilder;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesKey;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.Tunnel;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import java.util.ArrayList;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Set;
42
43 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
44 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbTerminationPoint;
45 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
46 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.submitToDs;
47
48 public class InventoryHelper {
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 =
61                 (Long.decode(HEX + addressInBytes[2]) << 40) |
62                 (Long.decode(HEX + addressInBytes[3]) << 32) |
63                 (Long.decode(HEX + addressInBytes[4]) << 24) |
64                 (Long.decode(HEX + addressInBytes[5]) << 16) |
65                 (Long.decode(HEX + addressInBytes[6]) << 8 ) |
66                 (Long.decode(HEX + addressInBytes[7]));
67         return address;
68     }
69
70     private static final Long MAX_OF_PORT=65534L;
71     /**
72      * Construct a String that can be used to create a
73      * {@link NodeId}.
74      * The String is constructed by getting the Datapath ID from the OVSDB bridge
75      * augmentation, converting that to a Long, and prepending it with the
76      * "openflow:" prefix.
77      *
78      * @param ovsdbBridge The OVSDB bridge augmentation
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      * @return String representation of the Inventory NodeConnectorId, null if it fails
110      */
111     public static String getInventoryNodeConnectorIdString(String inventoryNodeId,
112                        OvsdbTerminationPointAugmentation ovsdbTp,
113                        InstanceIdentifier<OvsdbTerminationPointAugmentation> tpIid,
114                        DataBroker dataBroker) {
115         Long ofport = null;
116         if (ovsdbTp.getOfport() != null && ovsdbTp.getOfport()>MAX_OF_PORT) {
117             LOG.debug("Invalid OpenFlow port {} for {}",ovsdbTp.getOfport(), ovsdbTp);
118             return null;
119         }
120         if (ovsdbTp.getOfport() == null) {
121             OvsdbTerminationPointAugmentation readOvsdbTp =
122                     getOvsdbTerminationPoint(tpIid, dataBroker);
123             if (readOvsdbTp == null
124                     || readOvsdbTp.getOfport() == null
125                     || readOvsdbTp.getOfport() >MAX_OF_PORT) {
126                 LOG.debug("Couldn't get OpenFlow port for {}",ovsdbTp);
127                 return null;
128             }
129             ofport = readOvsdbTp.getOfport();
130         } else {
131             ofport = ovsdbTp.getOfport();
132         }
133         String nodeConnectorIdString = inventoryNodeId + ":" + String.valueOf(ofport);
134
135         if(StringUtils.countMatches(nodeConnectorIdString, ":") != 2) {
136             LOG.error("{} is not correct format for NodeConnectorId.",nodeConnectorIdString);
137             return null;
138         }
139         return nodeConnectorIdString;
140     }
141
142     /**
143      * Read the {@link OfOverlayNodeConfig} augmentation from the
144      * Inventory Node, and verify that the tunnel types we need
145      * are present
146      *
147      * @return true if tunnel types are present, false otherwise
148      */
149     public static boolean checkOfOverlayConfig(String nodeIdString,
150                        List<AbstractTunnelType> requiredTunnelTypes, DataBroker dataBroker) {
151         OfOverlayNodeConfig config = getOfOverlayConfig(nodeIdString, dataBroker);
152         if (config == null || config.getTunnel() == null) {
153             LOG.debug("No OfOverlay config for {}",nodeIdString);
154             return false;
155         }
156
157         /*
158          * See if the OfOverlayNodeConfig has the
159          * tunnel type information.
160          */
161         for (AbstractTunnelType tunnelType: requiredTunnelTypes) {
162             boolean tunnelPresent = false;
163             for (Tunnel tunnel: config.getTunnel()) {
164                 if (tunnelType.getTunnelType().equals(tunnel.getTunnelType())) {
165                     tunnelPresent = true;
166                     break;
167                 }
168             }
169             if (tunnelPresent == false) {
170                 return false;
171             }
172         }
173         return true;
174     }
175
176     public static void addOfOverlayExternalPort(String nodeIdString, NodeConnectorId ncId, DataBroker dataBroker) {
177         InstanceIdentifier<ExternalInterfaces> nodeExternalInterfacesIid = InstanceIdentifier.builder(
178                 Nodes.class)
179             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
180             .augmentation(OfOverlayNodeConfig.class)
181             .child(ExternalInterfaces.class,new ExternalInterfacesKey(ncId))
182             .build();
183
184         ExternalInterfaces externalInterfaces = new ExternalInterfacesBuilder()
185                                                 .setKey(new ExternalInterfacesKey(ncId))
186                                                 .setNodeConnectorId(ncId)
187                                                 .build();
188         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
189         transaction.put(LogicalDatastoreType.CONFIGURATION, nodeExternalInterfacesIid, externalInterfaces, true);
190         submitToDs(transaction);
191         LOG.trace("Added external interface node connector {} to node {}", ncId.getValue(),nodeIdString);
192     }
193
194     public static OfOverlayNodeConfig getOfOverlayConfig(String nodeIdString, DataBroker dataBroker) {
195         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(
196                 Nodes.class)
197             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
198             .augmentation(OfOverlayNodeConfig.class)
199             .build();
200
201         ReadWriteTransaction transaction = dataBroker.newReadWriteTransaction();
202         Optional<OfOverlayNodeConfig> overlayConfig = readFromDs(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, transaction );
203         if (overlayConfig.isPresent()) {
204             return overlayConfig.get();
205         }
206         return null;
207     }
208
209     private static void addOfOverlayAugmentation(OfOverlayNodeConfig config, String nodeIdString, DataBroker dataBroker) {
210         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(
211                 Nodes.class)
212             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
213             .augmentation(OfOverlayNodeConfig.class)
214             .build();
215
216         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
217         transaction.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, config, true);
218         submitToDs(transaction);
219     }
220
221     private static void addTunnelToOfOverlayAugmentation(Tunnel tunnel, String nodeIdString, DataBroker dataBroker) {
222         InstanceIdentifier<Tunnel> ofOverlayNodeIid = InstanceIdentifier.builder(
223                 Nodes.class)
224             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
225             .augmentation(OfOverlayNodeConfig.class)
226             .child(Tunnel.class, new TunnelKey(tunnel.getKey()))
227             .build();
228
229         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
230         transaction.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, tunnel, true);
231         if(submitToDs(transaction)) {
232             LOG.info("addTunnelToOfOverlayAugmentation: Wrote tunnel {}",tunnel.getKey());
233         } else {
234             LOG.error("addTunnelToOfOverlayAugmentation: submitToDs failed for tunnel {}",tunnel.getKey());
235         }
236     }
237
238     /**
239      * Update the {@link OfOverlayConfig} of an Inventory Node
240      * using the new tunnel state.
241      *
242      * @param nodeIdString
243      * @param tunnels
244      * @param dataBroker
245      */
246     public static void updateOfOverlayConfig(IpAddress ip, String nodeIdString,
247             String nodeConnectorIdString, List<AbstractTunnelType> tunnels, DataBroker dataBroker) {
248
249         if ((ip == null) || (nodeIdString == null)
250                 || (nodeConnectorIdString == null)) {
251             LOG.debug("Can't update OfOverlay: requisite information not present");
252             return;
253         }
254         NodeConnectorId nodeConnectorId = new NodeConnectorId(nodeConnectorIdString);
255         List<Tunnel> tunnelList = new ArrayList<Tunnel>();
256         List<Tunnel> existingTunnels = new ArrayList<Tunnel>();
257         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
258         if (ofConfig != null && ofConfig.getTunnel() != null) {
259             existingTunnels = ofConfig.getTunnel();
260         }
261         boolean tunnelsUpdated = false;
262         for (AbstractTunnelType tunnelType: tunnels) {
263             boolean tunnelFound = false;
264             for (Tunnel currentTun: existingTunnels) {
265                 if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
266                     // tunnel update
267                     TunnelBuilder tunnelBuilder = new TunnelBuilder(currentTun);
268                     tunnelBuilder.setIp(ip);
269                     tunnelBuilder.setPort(tunnelType.getPortNumber());
270                     tunnelBuilder.setNodeConnectorId(nodeConnectorId);
271                     tunnelList.add(tunnelBuilder.build());
272                     tunnelFound = true;
273                     tunnelsUpdated = true;
274                 }
275             }
276             // new tunnel
277             if (tunnelFound == false) {
278                 TunnelBuilder tunnelBuilder = new TunnelBuilder();
279                 tunnelBuilder.setIp(ip);
280                 tunnelBuilder.setPort(tunnelType.getPortNumber());
281                 tunnelBuilder.setNodeConnectorId(nodeConnectorId);
282                 tunnelBuilder.setTunnelType(tunnelType.getTunnelType());
283                 tunnelList.add(tunnelBuilder.build());
284                 tunnelsUpdated = true;
285             }
286         }
287         if (tunnelsUpdated == true) {
288             for (Tunnel tunnel: tunnelList) {
289                 addTunnelToOfOverlayAugmentation(tunnel, nodeIdString, dataBroker);
290             }
291         }
292     }
293
294     public static void removeTunnelsOfOverlayConfig(String nodeIdString,
295                                                     List<AbstractTunnelType> tunnels,
296                                                     DataBroker dataBroker) {
297
298         if (nodeIdString == null) {
299             LOG.debug("Can't update OfOverlay: requisite information not present");
300             return;
301         }
302         List<Tunnel> existingTunnels = new ArrayList<>();
303         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
304         if (ofConfig != null) {
305             existingTunnels = ofConfig.getTunnel();
306         }
307         Set<Tunnel> tunnelsToRemove = new HashSet<>();
308         for (AbstractTunnelType tunnelType : tunnels) {
309             for (Tunnel currentTun : existingTunnels) {
310                 if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
311                     tunnelsToRemove.add(currentTun);
312                 }
313             }
314         }
315
316         // runs only if some tunnels were really removed
317         if (existingTunnels.removeAll(tunnelsToRemove)) {
318             OfOverlayNodeConfigBuilder ofOverlayBuilder;
319             if (ofConfig == null) {
320                 ofOverlayBuilder = new OfOverlayNodeConfigBuilder();
321             } else {
322                 ofOverlayBuilder = new OfOverlayNodeConfigBuilder(ofConfig);
323             }
324             ofOverlayBuilder.setTunnel(existingTunnels);
325             addOfOverlayAugmentation(ofOverlayBuilder.build(), nodeIdString, dataBroker);
326         }
327     }
328 }