2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.groupbasedpolicy.neutron.ovsdb.util;
10 import com.google.common.base.Optional;
11 import java.util.HashSet;
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;
40 import java.util.ArrayList;
41 import java.util.HashSet;
42 import java.util.List;
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;
50 public class InventoryHelper {
52 private static final Logger LOG = LoggerFactory.getLogger(InventoryHelper.class);
53 private static final String HEX = "0x";
56 * Convert an OpenFlow Datapath ID to a Long
58 * @param dpid The OpenFlow Datapath ID
59 * @return The Long representation of the DPID
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]));
69 private static final Long MAX_OF_PORT = 65534L;
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
77 * @param ovsdbBridge The OVSDB bridge augmentation
78 * @return String representation of the Inventory NodeId, null if it fails
80 public static String getInventoryNodeIdString(OvsdbBridgeAugmentation ovsdbBridge,
81 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid, DataBroker dataBroker) {
82 DatapathId dpid = ovsdbBridge.getDatapathId();
84 OvsdbBridgeAugmentation bridgeData = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
85 dpid = bridgeData.getDatapathId();
87 LOG.error("No Data Path ID for OVSDB Bridge {}", ovsdbBridge);
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);
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.
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
110 public static String getInventoryNodeConnectorIdString(String inventoryNodeId,
111 OvsdbTerminationPointAugmentation ovsdbTp, InstanceIdentifier<OvsdbTerminationPointAugmentation> tpIid,
112 DataBroker dataBroker) {
114 if (ovsdbTp.getOfport() != null && ovsdbTp.getOfport() > MAX_OF_PORT) {
115 LOG.debug("Invalid OpenFlow port {} for {}", ovsdbTp.getOfport(), ovsdbTp);
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);
124 ofport = readOvsdbTp.getOfport();
126 ofport = ovsdbTp.getOfport();
128 String nodeConnectorIdString = inventoryNodeId + ":" + String.valueOf(ofport);
130 if (StringUtils.countMatches(nodeConnectorIdString, ":") != 2) {
131 LOG.error("{} is not correct format for NodeConnectorId.", nodeConnectorIdString);
134 return nodeConnectorIdString;
138 * Read the {@link OfOverlayNodeConfig} augmentation from the
139 * Inventory Node, and verify that the tunnel types we need
142 * @return true if tunnel types are present, false otherwise
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);
153 * See if the OfOverlayNodeConfig has the
154 * tunnel type information.
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;
164 if (tunnelPresent == false) {
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))
178 ExternalInterfaces externalInterfaces = new ExternalInterfacesBuilder().setKey(new ExternalInterfacesKey(ncId))
179 .setNodeConnectorId(ncId)
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);
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)
193 ReadWriteTransaction transaction = dataBroker.newReadWriteTransaction();
194 Optional<OfOverlayNodeConfig> overlayConfig = readFromDs(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid,
196 if (overlayConfig.isPresent()) {
197 return overlayConfig.get();
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)
208 WriteTransaction transaction = dataBroker.newReadWriteTransaction();
209 transaction.put(LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, config, true);
210 return submitToDs(transaction);
214 * Update the {@link OfOverlayConfig} of an Inventory Node
215 * using the new tunnel state.
218 * @param nodeIdString
219 * @param nodeConnectorIdString
223 public static void updateOfOverlayConfig(IpAddress ip, String nodeIdString, String nodeConnectorIdString,
224 AbstractTunnelType tunnelType, DataBroker dataBroker) {
226 if ((ip == null) || (nodeIdString == null) || (nodeConnectorIdString == null)) {
227 LOG.debug("Can't update OfOverlay: requisite information not present");
230 NodeConnectorId nodeConnectorId = new NodeConnectorId(nodeConnectorIdString);
233 // Pull existing augmentation
234 OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
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());
245 ofOverlayNodeConfigBuilder = new OfOverlayNodeConfigBuilder();
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();
252 boolean tunnelFound = false;
253 for (Tunnel currentTun : existingTunnels) {
254 if (tunnelType.getTunnelType().equals(currentTun.getTunnelType())) {
256 tunnelBuilder.setIp(ip);
257 tunnelBuilder.setPort(tunnelType.getPortNumber());
258 tunnelBuilder.setNodeConnectorId(nodeConnectorId);
260 tunnelsUpdated = true;
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;
273 // Nothing was updated, nothing to see here, move along...
274 if (tunnelsUpdated == false) {
278 existingTunnels.add(tunnelBuilder.build());
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);
286 LOG.error("updateOfOverlayConfig - could not write OfOverlayNodeConfig: {} to datastore.", newConfig);
290 public static void removeTunnelsOfOverlayConfig(String nodeIdString,
291 List<AbstractTunnelType> tunnels,
292 DataBroker dataBroker) {
294 if (nodeIdString == null) {
295 LOG.debug("Can't update OfOverlay: requisite information not present");
298 List<Tunnel> existingTunnels = new ArrayList<>();
299 OfOverlayNodeConfig ofConfig = getOfOverlayConfig(nodeIdString, dataBroker);
300 if (ofConfig != null) {
301 existingTunnels = ofConfig.getTunnel();
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);
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();
318 ofOverlayBuilder = new OfOverlayNodeConfigBuilder(ofConfig);
320 ofOverlayBuilder.setTunnel(existingTunnels);
321 addOfOverlayAugmentation(ofOverlayBuilder.build(), nodeIdString, dataBroker);