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