Possible fix for OfOverlay overwrite (#2).
[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.util.DataStoreHelper.readFromDs;
11 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.submitToDs;
12 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
13 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbTerminationPoint;
14
15 import java.util.ArrayList;
16 import java.util.List;
17
18 import org.apache.commons.lang3.StringUtils;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.AbstractTunnelType;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfigBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfacesKey;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.Tunnel;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.common.base.Optional;
46
47 public class InventoryHelper {
48     private static final Logger LOG = LoggerFactory.getLogger(InventoryHelper.class);
49     private static final String HEX = "0x";
50
51     /**
52      * Convert an OpenFlow Datapath ID to a Long
53      *
54      * @param dpid The OpenFlow Datapath ID
55      * @return The Long representation of the DPID
56      */
57     public static Long getLongFromDpid(String dpid) {
58         String[] addressInBytes = dpid.split(":");
59         Long address =
60                 (Long.decode(HEX + addressInBytes[2]) << 40) |
61                 (Long.decode(HEX + addressInBytes[3]) << 32) |
62                 (Long.decode(HEX + addressInBytes[4]) << 24) |
63                 (Long.decode(HEX + addressInBytes[5]) << 16) |
64                 (Long.decode(HEX + addressInBytes[6]) << 8 ) |
65                 (Long.decode(HEX + addressInBytes[7]));
66         return address;
67     }
68
69     private static final Long MAX_OF_PORT=65534L;
70     /**
71      * Construct a String that can be used to create a
72      * {@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,
112                        InstanceIdentifier<OvsdbTerminationPointAugmentation> tpIid,
113                        DataBroker dataBroker) {
114         Long ofport = null;
115         if (ovsdbTp.getOfport() != null && ovsdbTp.getOfport()>MAX_OF_PORT) {
116             LOG.debug("Invalid OpenFlow port {} for {}",ovsdbTp.getOfport(), ovsdbTp);
117             return null;
118         }
119         if (ovsdbTp.getOfport() == null) {
120             OvsdbTerminationPointAugmentation readOvsdbTp =
121                     getOvsdbTerminationPoint(tpIid, dataBroker);
122             if (readOvsdbTp == null
123                     || readOvsdbTp.getOfport() == null
124                     || 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      * @return true if tunnel types are present, false otherwise
147      */
148     public static boolean checkOfOverlayConfig(String nodeIdString,
149                        List<AbstractTunnelType> requiredTunnelTypes, DataBroker dataBroker) {
150         OfOverlayNodeConfig config = getOfOverlayConfig(nodeIdString, dataBroker);
151         if (config == null || config.getTunnel() == null) {
152             LOG.debug("No OfOverlay config for {}",nodeIdString);
153             return false;
154         }
155
156         /*
157          * See if the OfOverlayNodeConfig has the
158          * tunnel type information.
159          */
160         for (AbstractTunnelType tunnelType: requiredTunnelTypes) {
161             boolean tunnelPresent = false;
162             for (Tunnel tunnel: config.getTunnel()) {
163                 if (tunnelType.getTunnelType().equals(tunnel.getTunnelType())) {
164                     tunnelPresent = true;
165                     break;
166                 }
167             }
168             if (tunnelPresent == false) {
169                 return false;
170             }
171         }
172         return true;
173     }
174
175     public static void addOfOverlayExternalPort(String nodeIdString, NodeConnectorId ncId, DataBroker dataBroker) {
176         InstanceIdentifier<ExternalInterfaces> nodeExternalInterfacesIid = InstanceIdentifier.builder(
177                 Nodes.class)
178             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
179             .augmentation(OfOverlayNodeConfig.class)
180             .child(ExternalInterfaces.class,new ExternalInterfacesKey(ncId))
181             .build();
182
183         ExternalInterfaces externalInterfaces = new ExternalInterfacesBuilder()
184                                                 .setKey(new ExternalInterfacesKey(ncId))
185                                                 .setNodeConnectorId(ncId)
186                                                 .build();
187         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
188         transaction.put(LogicalDatastoreType.CONFIGURATION, nodeExternalInterfacesIid, externalInterfaces, true);
189         submitToDs(transaction);
190         LOG.trace("Added external interface node connector {} to node {}", ncId.getValue(),nodeIdString);
191     }
192
193     public static OfOverlayNodeConfig getOfOverlayConfig(String nodeIdString, DataBroker dataBroker) {
194         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(
195                 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.OPERATIONAL, ofOverlayNodeIid, transaction );
202         if (overlayConfig.isPresent()) {
203             return overlayConfig.get();
204         }
205         return null;
206     }
207
208     private static void addOfOverlayAugmentation(OfOverlayNodeConfig config, String nodeIdString, DataBroker dataBroker) {
209         InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(
210                 Nodes.class)
211             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
212             .augmentation(OfOverlayNodeConfig.class)
213             .build();
214
215         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
216         transaction.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, config, true);
217         submitToDs(transaction);
218     }
219
220     private static void addTunnelToOfOverlayAugmentation(Tunnel tunnel, String nodeIdString, DataBroker dataBroker) {
221         InstanceIdentifier<Tunnel> ofOverlayNodeIid = InstanceIdentifier.builder(
222                 Nodes.class)
223             .child(Node.class, new NodeKey(new NodeId(nodeIdString)))
224             .augmentation(OfOverlayNodeConfig.class)
225             .child(Tunnel.class, new TunnelKey(tunnel.getKey()))
226             .build();
227
228         WriteTransaction transaction = dataBroker.newReadWriteTransaction();
229         transaction.merge(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, tunnel, true);
230         submitToDs(transaction);
231     }
232
233     /**
234      * Update the {@link OfOverlayConfig} of an Inventory Node
235      * using the new tunnel state.
236      *
237      * @param nodeIdString
238      * @param tunnels
239      * @param dataBroker
240      */
241     public static void updateOfOverlayConfig(IpAddress ip, String nodeIdString,
242             String nodeConnectorIdString, List<AbstractTunnelType> tunnels, DataBroker dataBroker) {
243
244         if ((ip == null) || (nodeIdString == null)
245                 || (nodeConnectorIdString == null)) {
246             LOG.debug("Can't update OfOverlay: requisite information not present");
247             return;
248         }
249         NodeConnectorId nodeConnectorId = new NodeConnectorId(nodeConnectorIdString);
250         List<Tunnel> tunnelList = new ArrayList<Tunnel>();
251         List<Tunnel> existingTunnels = new ArrayList<Tunnel>();
252         OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
253         if (ofConfig != null) {
254             existingTunnels = ofConfig.getTunnel();
255         }
256         boolean tunnelsUpdated = false;
257         for (AbstractTunnelType tunnelType: tunnels) {
258             boolean tunnelFound = false;
259             for (Tunnel currentTun: existingTunnels) {
260                 if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
261                     // tunnel update
262                     TunnelBuilder tunnelBuilder = new TunnelBuilder(currentTun);
263                     tunnelBuilder.setIp(ip);
264                     tunnelBuilder.setPort(tunnelType.getPortNumber());
265                     tunnelBuilder.setNodeConnectorId(nodeConnectorId);
266                     tunnelList.add(tunnelBuilder.build());
267                     tunnelFound = true;
268                     tunnelsUpdated = true;
269                 }
270             }
271             // new tunnel
272             if (tunnelFound == false) {
273                 TunnelBuilder tunnelBuilder = new TunnelBuilder();
274                 tunnelBuilder.setIp(ip);
275                 tunnelBuilder.setPort(tunnelType.getPortNumber());
276                 tunnelBuilder.setNodeConnectorId(nodeConnectorId);
277                 tunnelBuilder.setTunnelType(tunnelType.getTunnelType());
278                 tunnelList.add(tunnelBuilder.build());
279                 tunnelsUpdated = true;
280             }
281         }
282         if (tunnelsUpdated == true) {
283             for (Tunnel tunnel: tunnelList) {
284                 addTunnelToOfOverlayAugmentation(tunnel, nodeIdString, dataBroker);
285             }
286         }
287     }
288 }