OF Flow programming on br-int (NORMAL) and bidirectional br-tun (based on tenant...
[ovsdb.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / provider / OF10ProviderManager.java
1 package org.opendaylight.ovsdb.neutron.provider;
2
3 import java.math.BigInteger;
4 import java.net.InetAddress;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
11 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
12 import org.opendaylight.controller.sal.action.ActionType;
13 import org.opendaylight.controller.sal.core.Node;
14 import org.opendaylight.controller.sal.utils.HexEncode;
15 import org.opendaylight.controller.sal.utils.ServiceHelper;
16 import org.opendaylight.controller.sal.utils.Status;
17 import org.opendaylight.controller.sal.utils.StatusCode;
18 import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
19 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
20 import org.opendaylight.ovsdb.lib.notation.UUID;
21 import org.opendaylight.ovsdb.lib.table.Bridge;
22 import org.opendaylight.ovsdb.lib.table.Interface;
23 import org.opendaylight.ovsdb.lib.table.Port;
24 import org.opendaylight.ovsdb.lib.table.internal.Table;
25 import org.opendaylight.ovsdb.neutron.AdminConfigManager;
26 import org.opendaylight.ovsdb.neutron.InternalNetworkManager;
27 import org.opendaylight.ovsdb.neutron.TenantNetworkManager;
28 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
29 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33
34 class OF10ProviderManager extends ProviderNetworkManager {
35     private static final Logger logger = LoggerFactory.getLogger(OF10ProviderManager.class);
36
37     @Override
38     public boolean hasPerTenantTunneling() {
39         return true;
40     }
41
42     private Status getTunnelReadinessStatus (Node node, String tunnelKey) {
43         InetAddress srcTunnelEndPoint = AdminConfigManager.getManager().getTunnelEndPoint(node);
44         if (srcTunnelEndPoint == null) {
45             logger.error("Tunnel Endpoint not configured for Node {}", node);
46             return new Status(StatusCode.NOTFOUND, "Tunnel Endpoint not configured for "+ node);
47         }
48
49         try {
50             if (!InternalNetworkManager.getManager().isInternalNetworkOverlayReady(node)) {
51                 logger.error(node+" is not Overlay ready");
52                 return new Status(StatusCode.NOTACCEPTABLE, node+" is not Overlay ready");
53             }
54         } catch (Exception e) {
55             logger.error(node+" is not Overlay ready");
56             return new Status(StatusCode.NOTACCEPTABLE, node+" is not Overlay ready");
57         }
58
59         if (!TenantNetworkManager.getManager().isTenantNetworkPresentInNode(node, tunnelKey)) {
60             logger.debug(node+" has no VM corresponding to segment "+ tunnelKey);
61             return new Status(StatusCode.NOTACCEPTABLE, node+" has no VM corresponding to segment "+ tunnelKey);
62         }
63         return new Status(StatusCode.SUCCESS);
64     }
65
66     private void programLocalIngressTunnelBridgeRules(Node node, int tunnelOFPort, String attachedMac,
67                                                       int internalVlan, int patchPort) {
68         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
69         if (brIntId == null) {
70             logger.error("Failed to initialize Flow Rules for {}", node);
71             return;
72         }
73         try {
74             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
75             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
76             Set<String> dpids = bridge.getDatapath_id();
77             if (dpids == null || dpids.size() ==  0) return;
78             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
79             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
80             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
81                     IForwardingRulesManager.class, "default", this);
82             FlowConfig flow = new FlowConfig();
83             flow.setName("TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac));
84             flow.setNode(ofNode);
85             flow.setPriority("100");
86             flow.setDstMac(attachedMac);
87             flow.setIngressPort(tunnelOFPort+"");
88             List<String> actions = new ArrayList<String>();
89             actions.add(ActionType.SET_VLAN_ID+"="+internalVlan);
90             actions.add(ActionType.OUTPUT.toString()+"="+patchPort);
91             flow.setActions(actions);
92             Status status = frm.addStaticFlow(flow);
93             logger.debug("Local Ingress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
94         } catch (Exception e) {
95             logger.error("Failed to initialize Flow Rules for {}", node, e);
96         }
97     }
98
99     private void programRemoteEgressTunnelBridgeRules(Node node, int patchPort, String attachedMac,
100             int internalVlan, int tunnelOFPort) {
101         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
102         if (brIntId == null) {
103             logger.error("Failed to initialize Flow Rules for {}", node);
104             return;
105         }
106         try {
107             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
108             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
109             Set<String> dpids = bridge.getDatapath_id();
110             if (dpids == null || dpids.size() ==  0) return;
111             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
112             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
113             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
114                     IForwardingRulesManager.class, "default", this);
115             FlowConfig flow = new FlowConfig();
116             flow.setName("TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac));
117             flow.setNode(ofNode);
118             flow.setPriority("100");
119             flow.setDstMac(attachedMac);
120             flow.setIngressPort(patchPort+"");
121             flow.setVlanId(internalVlan+"");
122             List<String> actions = new ArrayList<String>();
123             actions.add(ActionType.POP_VLAN.toString());
124             actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
125             flow.setActions(actions);
126             Status status = frm.addStaticFlow(flow);
127             logger.debug("Remote Egress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
128         } catch (Exception e) {
129             logger.error("Failed to initialize Flow Rules for {}", node, e);
130         }
131     }
132
133     private void programTunnelRules (String tunnelType, String segmentationId, InetAddress dst, Node node,
134                                      Interface intf, boolean local) {
135         String networkId = TenantNetworkManager.getManager().getNetworkIdForSegmentationId(segmentationId);
136         if (networkId == null) {
137             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
138             return;
139         }
140         int internalVlan = TenantNetworkManager.getManager().getInternalVlan(networkId);
141         if (internalVlan == 0) {
142             logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
143             return;
144         }
145         Map<String, String> externalIds = intf.getExternal_ids();
146         if (externalIds == null) {
147             logger.error("No external_ids seen in {}", intf);
148             return;
149         }
150
151         String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
152         if (attachedMac == null) {
153             logger.error("No AttachedMac seen in {}", intf);
154             return;
155         }
156         String patchInt = "";
157         if (local) {
158             patchInt = AdminConfigManager.getManager().getPatchToIntegration();
159         } else {
160             patchInt = AdminConfigManager.getManager().getPatchToTunnel();
161         }
162
163         int patchOFPort = -1;
164         try {
165             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
166             Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
167             if (intfs != null) {
168                 for (Table<?> row : intfs.values()) {
169                     Interface patchIntf = (Interface)row;
170                     if (patchIntf.getName().equalsIgnoreCase(patchInt)) {
171                         Set<BigInteger> of_ports = patchIntf.getOfport();
172                         if (of_ports == null || of_ports.size() <= 0) {
173                             logger.error("Could NOT Identified Patch port {} -> OF ({}) on {}", patchInt, node);
174                             continue;
175                         }
176                         patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
177                         logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
178                         break;
179                     }
180                 }
181                 if (patchOFPort == -1) {
182                     logger.error("Cannot identify {} interface on {}", patchInt, node);
183                 }
184                 for (Table<?> row : intfs.values()) {
185                     Interface tunIntf = (Interface)row;
186                     if (tunIntf.getName().equals(this.getTunnelName(tunnelType, segmentationId, dst))) {
187                         Set<BigInteger> of_ports = tunIntf.getOfport();
188                         if (of_ports == null || of_ports.size() <= 0) {
189                             logger.error("Could NOT Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), node);
190                             continue;
191                         }
192                         int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
193                         logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
194
195                         if (local) {
196                             programLocalIngressTunnelBridgeRules(node, tunnelOFPort, attachedMac, internalVlan, patchOFPort);
197                         } else {
198                             programRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
199                         }
200                         return;
201                     }
202                 }
203             }
204         } catch (Exception e) {
205             logger.error("", e);
206         }
207     }
208     @Override
209     public Status createTunnels(String tunnelType, String tunnelKey, Node srcNode, Interface intf) {
210         Status status = getTunnelReadinessStatus(srcNode, tunnelKey);
211         if (!status.isSuccess()) return status;
212
213         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
214         List<Node> nodes = connectionService.getNodes();
215         nodes.remove(srcNode);
216         for (Node dstNode : nodes) {
217             status = getTunnelReadinessStatus(dstNode, tunnelKey);
218             if (!status.isSuccess()) continue;
219             InetAddress src = AdminConfigManager.getManager().getTunnelEndPoint(srcNode);
220             InetAddress dst = AdminConfigManager.getManager().getTunnelEndPoint(dstNode);
221             status = addTunnelPort(srcNode, tunnelType, src, dst, tunnelKey);
222             if (status.isSuccess()) {
223                 this.programTunnelRules(tunnelType, tunnelKey, dst, srcNode, intf, true);
224             }
225             addTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
226             if (status.isSuccess()) {
227                 this.programTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
228             }
229         }
230         return new Status(StatusCode.SUCCESS);
231     }
232
233
234     private String getTunnelName(String tunnelType, String key, InetAddress dst) {
235         return tunnelType+"-"+key+"-"+dst.getHostAddress();
236     }
237
238     private Interface getTunnelInterface (Node node, String tunnelType, InetAddress dst, String key) {
239         try {
240             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
241             String portName = getTunnelName(tunnelType, key, dst);
242
243             Map<String, Table<?>> tunIntfs = ovsdbTable.getRows(node, Interface.NAME.getName());
244             if (tunIntfs != null) {
245                 for (Table<?> row : tunIntfs.values()) {
246                     Interface tunIntf = (Interface)row;
247                     if (tunIntf.getName().equals(portName)) return tunIntf;
248                 }
249
250             }
251         } catch (Exception e) {
252             logger.error("", e);
253         }
254         return null;
255     }
256
257     private boolean isTunnelPresent(Node node, String tunnelName, String bridgeUUID) throws Exception {
258         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
259         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
260         if (bridge != null) {
261             Set<UUID> ports = bridge.getPorts();
262             for (UUID portUUID : ports) {
263                 Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID.toString());
264                 if (port != null && port.getName().equalsIgnoreCase(tunnelName)) return true;
265             }
266         }
267         return false;
268     }
269
270     private Status addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
271         try {
272             String bridgeUUID = null;
273             String tunnelBridgeName = AdminConfigManager.getManager().getTunnelBridgeName();
274             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
275             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
276             if (bridgeTable != null) {
277                 for (String uuid : bridgeTable.keySet()) {
278                     Bridge bridge = (Bridge)bridgeTable.get(uuid);
279                     if (bridge.getName().equals(tunnelBridgeName)) {
280                         bridgeUUID = uuid;
281                         break;
282                     }
283                 }
284             }
285             if (bridgeUUID == null) {
286                 logger.error("Could not find Bridge {} in {}", tunnelBridgeName, node);
287                 return new Status(StatusCode.NOTFOUND, "Could not find "+tunnelBridgeName+" in "+node);
288             }
289             String portName = getTunnelName(tunnelType, key, dst);
290
291             if (this.isTunnelPresent(node, portName, bridgeUUID)) {
292                 logger.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node);
293                 return new Status(StatusCode.SUCCESS);
294             }
295
296             Port tunnelPort = new Port();
297             tunnelPort.setName(portName);
298             Status status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, tunnelPort);
299             if (!status.isSuccess()) {
300                 logger.error("Failed to insert Tunnel port {} in {}", portName, bridgeUUID);
301                 return status;
302             }
303
304             String tunnelPortUUID = status.getDescription();
305
306             String interfaceUUID = null;
307             int timeout = 6;
308             while ((interfaceUUID == null) && (timeout > 0)) {
309                 tunnelPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), tunnelPortUUID);
310                 OvsDBSet<UUID> interfaces = tunnelPort.getInterfaces();
311                 if (interfaces == null || interfaces.size() == 0) {
312                     // Wait for the OVSDB update to sync up the Local cache.
313                     Thread.sleep(500);
314                     timeout--;
315                     continue;
316                 }
317                 interfaceUUID = interfaces.toArray()[0].toString();
318                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
319                 if (intf == null) interfaceUUID = null;
320             }
321
322             if (interfaceUUID == null) {
323                 logger.error("Cannot identify Tunnel Interface for port {}/{}", portName, tunnelPortUUID);
324                 return new Status(StatusCode.INTERNALERROR);
325             }
326
327             Interface tunInterface = new Interface();
328             tunInterface.setType(tunnelType);
329             OvsDBMap<String, String> options = new OvsDBMap<String, String>();
330             options.put("key", key);
331             options.put("local_ip", src.getHostAddress());
332             options.put("remote_ip", dst.getHostAddress());
333             tunInterface.setOptions(options);
334             status = ovsdbTable.updateRow(node, Interface.NAME.getName(), tunnelPortUUID, interfaceUUID, tunInterface);
335             logger.debug("Tunnel {} add status : {}", tunInterface, status);
336             return status;
337         } catch (Exception e) {
338             e.printStackTrace();
339             return new Status(StatusCode.INTERNALERROR);
340         }
341     }
342
343     @Override
344     public Status createTunnels(String tunnelType, String tunnelKey) {
345         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
346         List<Node> nodes = connectionService.getNodes();
347         for (Node srcNode : nodes) {
348             this.createTunnels(tunnelType, tunnelKey, srcNode, null);
349         }
350         return new Status(StatusCode.SUCCESS);
351     }
352 }