2 * Copyright (C) 2013 Red Hat, Inc.
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 * Authors : Madhu Venugopal, Brent Salisbury
10 package org.opendaylight.ovsdb.neutron.provider;
12 import java.math.BigInteger;
13 import java.net.InetAddress;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
20 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
21 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
22 import org.opendaylight.controller.sal.action.ActionType;
23 import org.opendaylight.controller.sal.core.Node;
24 import org.opendaylight.controller.sal.utils.HexEncode;
25 import org.opendaylight.controller.sal.utils.ServiceHelper;
26 import org.opendaylight.controller.sal.utils.Status;
27 import org.opendaylight.controller.sal.utils.StatusCode;
28 import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
29 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
30 import org.opendaylight.ovsdb.lib.notation.UUID;
31 import org.opendaylight.ovsdb.lib.table.Bridge;
32 import org.opendaylight.ovsdb.lib.table.Interface;
33 import org.opendaylight.ovsdb.lib.table.Port;
34 import org.opendaylight.ovsdb.lib.table.internal.Table;
35 import org.opendaylight.ovsdb.neutron.AdminConfigManager;
36 import org.opendaylight.ovsdb.neutron.InternalNetworkManager;
37 import org.opendaylight.ovsdb.neutron.TenantNetworkManager;
38 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
39 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
40 import org.opendaylight.ovsdb.plugin.StatusWithUuid;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 class OF10ProviderManager extends ProviderNetworkManager {
46 private static final Logger logger = LoggerFactory.getLogger(OF10ProviderManager.class);
47 Map<NodeVlan, FlowConfig> floodEntries = new HashMap<NodeVlan, FlowConfig>();
48 private static final int INGRESS_TUNNEL_FLOW_PRIORITY = 100;
49 private static final int EGRESS_TUNNEL_FLOW_PRIORITY = 100;
50 private static final int FLOOD_TUNNEL_FLOW_PRIORITY = 1;
53 public boolean hasPerTenantTunneling() {
57 private Status getTunnelReadinessStatus (Node node, String tunnelKey) {
58 InetAddress srcTunnelEndPoint = AdminConfigManager.getManager().getTunnelEndPoint(node);
59 if (srcTunnelEndPoint == null) {
60 logger.error("Tunnel Endpoint not configured for Node {}", node);
61 return new Status(StatusCode.NOTFOUND, "Tunnel Endpoint not configured for "+ node);
64 if (!InternalNetworkManager.getManager().isInternalNetworkOverlayReady(node)) {
65 logger.error(node+" is not Overlay ready");
66 return new Status(StatusCode.NOTACCEPTABLE, node+" is not Overlay ready");
69 if (!TenantNetworkManager.getManager().isTenantNetworkPresentInNode(node, tunnelKey)) {
70 logger.debug(node+" has no VM corresponding to segment "+ tunnelKey);
71 return new Status(StatusCode.NOTACCEPTABLE, node+" has no VM corresponding to segment "+ tunnelKey);
73 return new Status(StatusCode.SUCCESS);
77 * Program OF1.0 Flow rules on br-tun on the ingress direction from the network towards the br-int.
78 * The logic is to simply match on the incoming tunnel OF-Port (which carries the TenantNetwork GRE-Key)
79 * and rewrite the Corresponding internal Vlan and pass it on to br-int via the patch port.
81 private void programLocalIngressTunnelBridgeRules(Node node, int tunnelOFPort, int internalVlan, int patchPort) {
82 String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
83 if (brIntId == null) {
84 logger.error("Failed to initialize Flow Rules for {}", node);
88 String flowName = "TepMatch"+tunnelOFPort+""+internalVlan;
89 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
90 Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
91 Set<String> dpids = bridge.getDatapath_id();
92 if (dpids == null || dpids.size() == 0) return;
93 Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
94 Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
95 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
96 IForwardingRulesManager.class, "default", this);
97 if (frm.getStaticFlow(flowName, ofNode) != null) {
98 logger.debug("Local Ingress Flow exists : {} for Flow {} on {} / {}", flowName, ofNode, node);
102 FlowConfig flow = new FlowConfig();
103 flow.setName(flowName);
104 flow.setNode(ofNode);
105 flow.setPriority(INGRESS_TUNNEL_FLOW_PRIORITY+"");
106 flow.setIngressPort(tunnelOFPort+"");
107 List<String> actions = new ArrayList<String>();
108 actions.add(ActionType.SET_VLAN_ID+"="+internalVlan);
109 actions.add(ActionType.OUTPUT.toString()+"="+patchPort);
110 flow.setActions(actions);
111 Status status = frm.addStaticFlow(flow);
112 logger.debug("Local Ingress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
113 } catch (Exception e) {
114 logger.error("Failed to initialize Flow Rules for {}", node, e);
119 * Program OF1.0 Flow rules on br-tun on the remote Node on its egress direction towards the overlay network
120 * for a VM (with the attachedMac).
121 * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
122 * and output the traffic to the appropriate GRE Tunnel (which carries the GRE-Key for that Tenant Network).
123 * Also perform the Strip-Vlan action.
125 private void programRemoteEgressTunnelBridgeRules(Node node, int patchPort, String attachedMac,
126 int internalVlan, int tunnelOFPort) {
127 String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
128 if (brIntId == null) {
129 logger.error("Failed to initialize Flow Rules for {}", node);
133 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
134 Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
135 Set<String> dpids = bridge.getDatapath_id();
136 if (dpids == null || dpids.size() == 0) return;
137 Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
138 Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
139 String flowName = "TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac);
140 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
141 IForwardingRulesManager.class, "default", this);
142 if (frm.getStaticFlow(flowName, ofNode) != null) {
143 logger.debug("Remote Egress Flow exists : {} for Flow {} on {} / {}", flowName, ofNode, node);
146 FlowConfig flow = new FlowConfig();
147 flow.setName(flowName);
148 flow.setNode(ofNode);
149 flow.setPriority(EGRESS_TUNNEL_FLOW_PRIORITY+"");
150 flow.setDstMac(attachedMac);
151 flow.setIngressPort(patchPort+"");
152 flow.setVlanId(internalVlan+"");
153 List<String> actions = new ArrayList<String>();
154 actions.add(ActionType.POP_VLAN.toString());
155 actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
156 flow.setActions(actions);
157 Status status = frm.addStaticFlow(flow);
158 logger.debug("Remote Egress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
159 } catch (Exception e) {
160 logger.error("Failed to initialize Flow Rules for {}", node, e);
165 * Program OF1.0 Flow rules to flood the broadcast & unknown-unicast traffic over br-tun on the egress direction
166 * towards the network on all the overlay tunnels that corresponds to the tenant network.
167 * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
168 * and output the traffic to all the GRE-Tunnels for this Tenant Network (which carries the GRE-Key).
169 * Also perform the Strip-Vlan action.
171 private void programFloodEgressTunnelBridgeRules(Node node, int patchPort, int internalVlan, int tunnelOFPort) {
172 String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
173 if (brIntId == null) {
174 logger.error("Failed to initialize Flow Rules for {}", node);
178 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
179 Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
180 Set<String> dpids = bridge.getDatapath_id();
181 if (dpids == null || dpids.size() == 0) return;
182 Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
183 Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
184 NodeVlan nv = new NodeVlan(ofNode, internalVlan);
185 FlowConfig existingFlowConfig = floodEntries.get(nv);
186 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
187 IForwardingRulesManager.class, "default", this);
188 FlowConfig flow = existingFlowConfig;
189 Status status = null;
191 flow = new FlowConfig();
192 flow.setName("TepFlood"+internalVlan);
193 flow.setNode(ofNode);
194 flow.setPriority(FLOOD_TUNNEL_FLOW_PRIORITY+"");
195 flow.setIngressPort(patchPort+"");
196 flow.setVlanId(internalVlan+"");
197 List<String> actions = new ArrayList<String>();
198 actions.add(ActionType.POP_VLAN.toString());
199 actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
200 flow.setActions(actions);
201 status = frm.addStaticFlow(flow);
202 logger.debug("Add Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
203 status, flow, ofNode, node);
205 flow = new FlowConfig(existingFlowConfig);
206 List<String> actions = flow.getActions();
207 String outputPort = ActionType.OUTPUT.toString()+"="+tunnelOFPort;
208 if (actions != null && !actions.contains(outputPort)) {
209 actions.add(outputPort);
210 flow.setActions(actions);
214 status = frm.modifyStaticFlow(flow);
215 logger.debug("Modify Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
216 status, flow, ofNode, node);
218 if (status.isSuccess()) {
219 floodEntries.put(nv, flow);
222 } catch (Exception e) {
223 logger.error("Failed to initialize Flow Rules for {}", node, e);
227 private void programTunnelRules (String tunnelType, String segmentationId, InetAddress dst, Node node,
228 Interface intf, boolean local) {
229 String networkId = TenantNetworkManager.getManager().getNetworkIdForSegmentationId(segmentationId);
230 if (networkId == null) {
231 logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
234 int internalVlan = TenantNetworkManager.getManager().getInternalVlan(networkId);
235 if (internalVlan == 0) {
236 logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
239 Map<String, String> externalIds = intf.getExternal_ids();
240 if (externalIds == null) {
241 logger.error("No external_ids seen in {}", intf);
245 String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
246 if (attachedMac == null) {
247 logger.error("No AttachedMac seen in {}", intf);
250 String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
252 int patchOFPort = -1;
254 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
255 Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
257 for (Table<?> row : intfs.values()) {
258 Interface patchIntf = (Interface)row;
259 if (patchIntf.getName().equalsIgnoreCase(patchInt)) {
260 Set<BigInteger> of_ports = patchIntf.getOfport();
261 if (of_ports == null || of_ports.size() <= 0) {
262 logger.error("Could NOT Identified Patch port {} on {}", patchInt, node);
265 patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
266 logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
270 if (patchOFPort == -1) {
271 logger.error("Cannot identify {} interface on {}", patchInt, node);
273 for (Table<?> row : intfs.values()) {
274 Interface tunIntf = (Interface)row;
275 if (tunIntf.getName().equals(this.getTunnelName(tunnelType, segmentationId, dst))) {
276 Set<BigInteger> of_ports = tunIntf.getOfport();
277 if (of_ports == null || of_ports.size() <= 0) {
278 logger.error("Could NOT Identify Tunnel port {} on {}", tunIntf.getName(), node);
281 int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
283 if (tunnelOFPort == -1) {
284 logger.error("Could NOT Identify Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
287 logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
290 programRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
292 programLocalIngressTunnelBridgeRules(node, tunnelOFPort, internalVlan, patchOFPort);
293 programFloodEgressTunnelBridgeRules(node, patchOFPort, internalVlan, tunnelOFPort);
298 } catch (Exception e) {
304 public Status createTunnels(String tunnelType, String tunnelKey, Node srcNode, Interface intf) {
305 Status status = getTunnelReadinessStatus(srcNode, tunnelKey);
306 if (!status.isSuccess()) return status;
308 IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
309 List<Node> nodes = connectionService.getNodes();
310 nodes.remove(srcNode);
311 for (Node dstNode : nodes) {
312 status = getTunnelReadinessStatus(dstNode, tunnelKey);
313 if (!status.isSuccess()) continue;
314 InetAddress src = AdminConfigManager.getManager().getTunnelEndPoint(srcNode);
315 InetAddress dst = AdminConfigManager.getManager().getTunnelEndPoint(dstNode);
316 status = addTunnelPort(srcNode, tunnelType, src, dst, tunnelKey);
317 if (status.isSuccess()) {
318 this.programTunnelRules(tunnelType, tunnelKey, dst, srcNode, intf, true);
320 addTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
321 if (status.isSuccess()) {
322 this.programTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
325 return new Status(StatusCode.SUCCESS);
328 private String getTunnelName(String tunnelType, String key, InetAddress dst) {
329 return tunnelType+"-"+key+"-"+dst.getHostAddress();
332 private Interface getTunnelInterface (Node node, String tunnelType, InetAddress dst, String key) {
334 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
335 String portName = getTunnelName(tunnelType, key, dst);
337 Map<String, Table<?>> tunIntfs = ovsdbTable.getRows(node, Interface.NAME.getName());
338 if (tunIntfs != null) {
339 for (Table<?> row : tunIntfs.values()) {
340 Interface tunIntf = (Interface)row;
341 if (tunIntf.getName().equals(portName)) return tunIntf;
345 } catch (Exception e) {
351 private boolean isTunnelPresent(Node node, String tunnelName, String bridgeUUID) throws Exception {
352 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
353 Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
354 if (bridge != null) {
355 Set<UUID> ports = bridge.getPorts();
356 for (UUID portUUID : ports) {
357 Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID.toString());
358 if (port != null && port.getName().equalsIgnoreCase(tunnelName)) return true;
364 private Status addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
366 String bridgeUUID = null;
367 String tunnelBridgeName = AdminConfigManager.getManager().getTunnelBridgeName();
368 OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
369 Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
370 if (bridgeTable != null) {
371 for (String uuid : bridgeTable.keySet()) {
372 Bridge bridge = (Bridge)bridgeTable.get(uuid);
373 if (bridge.getName().equals(tunnelBridgeName)) {
379 if (bridgeUUID == null) {
380 logger.error("Could not find Bridge {} in {}", tunnelBridgeName, node);
381 return new Status(StatusCode.NOTFOUND, "Could not find "+tunnelBridgeName+" in "+node);
383 String portName = getTunnelName(tunnelType, key, dst);
385 if (this.isTunnelPresent(node, portName, bridgeUUID)) {
386 logger.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node);
387 return new Status(StatusCode.SUCCESS);
390 Port tunnelPort = new Port();
391 tunnelPort.setName(portName);
392 StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, tunnelPort);
393 if (!statusWithUuid.isSuccess()) {
394 logger.error("Failed to insert Tunnel port {} in {}", portName, bridgeUUID);
395 return statusWithUuid;
398 String tunnelPortUUID = statusWithUuid.getUuid().toString();
399 String interfaceUUID = null;
401 while ((interfaceUUID == null) && (timeout > 0)) {
402 tunnelPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), tunnelPortUUID);
403 OvsDBSet<UUID> interfaces = tunnelPort.getInterfaces();
404 if (interfaces == null || interfaces.size() == 0) {
405 // Wait for the OVSDB update to sync up the Local cache.
410 interfaceUUID = interfaces.toArray()[0].toString();
411 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
412 if (intf == null) interfaceUUID = null;
415 if (interfaceUUID == null) {
416 logger.error("Cannot identify Tunnel Interface for port {}/{}", portName, tunnelPortUUID);
417 return new Status(StatusCode.INTERNALERROR);
420 Interface tunInterface = new Interface();
421 tunInterface.setType(tunnelType);
422 OvsDBMap<String, String> options = new OvsDBMap<String, String>();
423 options.put("key", key);
424 options.put("local_ip", src.getHostAddress());
425 options.put("remote_ip", dst.getHostAddress());
426 tunInterface.setOptions(options);
427 Status status = ovsdbTable.updateRow(node, Interface.NAME.getName(), tunnelPortUUID, interfaceUUID, tunInterface);
428 logger.debug("Tunnel {} add status : {}", tunInterface, status);
430 } catch (Exception e) {
431 logger.error("Exception in addTunnelPort", e);
432 return new Status(StatusCode.INTERNALERROR);
437 public Status createTunnels(String tunnelType, String tunnelKey) {
438 IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
439 List<Node> nodes = connectionService.getNodes();
440 for (Node srcNode : nodes) {
441 this.createTunnels(tunnelType, tunnelKey, srcNode, null);
443 return new Status(StatusCode.SUCCESS);
446 private class NodeVlan {
449 public NodeVlan(Node node, int vlan) {
454 public Node getNode() {
457 public int getVlan() {
461 public String toString() {
462 return "NodeVlan [node=" + node + ", vlan=" + vlan + "]";
465 public int hashCode() {
466 final int prime = 31;
468 result = prime * result + ((node == null) ? 0 : node.hashCode());
469 result = prime * result + vlan;
473 public boolean equals(Object obj) {
478 if (getClass() != obj.getClass())
480 NodeVlan other = (NodeVlan) obj;
482 if (other.node != null)
484 } else if (!node.equals(other.node))
486 if (vlan != other.vlan)