f5885bc4da6342531121a178fd7641d70388e0b0
[netvirt.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / provider / OF10ProviderManager.java
1 /*
2  * Copyright (C) 2013 Red Hat, Inc.
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  * Authors : Madhu Venugopal, Brent Salisbury
9  */
10 package org.opendaylight.ovsdb.neutron.provider;
11
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;
17 import java.util.Map;
18 import java.util.Set;
19
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;
43
44
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;
51
52     @Override
53     public boolean hasPerTenantTunneling() {
54         return true;
55     }
56
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);
62         }
63
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");
67         }
68
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);
72         }
73         return new Status(StatusCode.SUCCESS);
74     }
75
76     /**
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.
80      */
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);
85             return;
86         }
87         try {
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);
99                 return;
100             }
101
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);
115         }
116     }
117
118     /**
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.
124      */
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);
130             return;
131         }
132         try {
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);
144                 return;
145             }
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);
161         }
162     }
163
164     /**
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.
170      */
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);
175             return;
176         }
177         try {
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;
190             if (flow == 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);
204             } else {
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);
211                 } else {
212                     return;
213                 }
214                 status = frm.modifyStaticFlow(flow);
215                 logger.debug("Modify Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
216                               status, flow, ofNode, node);
217             }
218             if (status.isSuccess()) {
219                 floodEntries.put(nv, flow);
220             }
221
222         } catch (Exception e) {
223             logger.error("Failed to initialize Flow Rules for {}", node, e);
224         }
225     }
226
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);
232             return;
233         }
234         int internalVlan = TenantNetworkManager.getManager().getInternalVlan(networkId);
235         if (internalVlan == 0) {
236             logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
237             return;
238         }
239         Map<String, String> externalIds = intf.getExternal_ids();
240         if (externalIds == null) {
241             logger.error("No external_ids seen in {}", intf);
242             return;
243         }
244
245         String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
246         if (attachedMac == null) {
247             logger.error("No AttachedMac seen in {}", intf);
248             return;
249         }
250         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
251
252         int patchOFPort = -1;
253         try {
254             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
255             Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
256             if (intfs != null) {
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);
263                             continue;
264                         }
265                         patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
266                         logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
267                         break;
268                     }
269                 }
270                 if (patchOFPort == -1) {
271                     logger.error("Cannot identify {} interface on {}", patchInt, node);
272                 }
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);
279                             continue;
280                         }
281                         int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
282
283                         if (tunnelOFPort == -1) {
284                             logger.error("Could NOT Identify Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
285                             return;
286                         }
287                         logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
288
289                         if (!local) {
290                             programRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
291                         }
292                         programLocalIngressTunnelBridgeRules(node, tunnelOFPort, internalVlan, patchOFPort);
293                         programFloodEgressTunnelBridgeRules(node, patchOFPort, internalVlan, tunnelOFPort);
294                         return;
295                     }
296                 }
297             }
298         } catch (Exception e) {
299             logger.error("", e);
300         }
301     }
302
303     @Override
304     public Status createTunnels(String tunnelType, String tunnelKey, Node srcNode, Interface intf) {
305         Status status = getTunnelReadinessStatus(srcNode, tunnelKey);
306         if (!status.isSuccess()) return status;
307
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);
319             }
320             addTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
321             if (status.isSuccess()) {
322                 this.programTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
323             }
324         }
325         return new Status(StatusCode.SUCCESS);
326     }
327
328     private String getTunnelName(String tunnelType, String key, InetAddress dst) {
329         return tunnelType+"-"+key+"-"+dst.getHostAddress();
330     }
331
332     private Interface getTunnelInterface (Node node, String tunnelType, InetAddress dst, String key) {
333         try {
334             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
335             String portName = getTunnelName(tunnelType, key, dst);
336
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;
342                 }
343
344             }
345         } catch (Exception e) {
346             logger.error("", e);
347         }
348         return null;
349     }
350
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;
359             }
360         }
361         return false;
362     }
363
364     private Status addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
365         try {
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)) {
374                         bridgeUUID = uuid;
375                         break;
376                     }
377                 }
378             }
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);
382             }
383             String portName = getTunnelName(tunnelType, key, dst);
384
385             if (this.isTunnelPresent(node, portName, bridgeUUID)) {
386                 logger.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node);
387                 return new Status(StatusCode.SUCCESS);
388             }
389
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;
396             }
397
398             String tunnelPortUUID = statusWithUuid.getUuid().toString();
399             String interfaceUUID = null;
400             int timeout = 6;
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.
406                     Thread.sleep(500);
407                     timeout--;
408                     continue;
409                 }
410                 interfaceUUID = interfaces.toArray()[0].toString();
411                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
412                 if (intf == null) interfaceUUID = null;
413             }
414
415             if (interfaceUUID == null) {
416                 logger.error("Cannot identify Tunnel Interface for port {}/{}", portName, tunnelPortUUID);
417                 return new Status(StatusCode.INTERNALERROR);
418             }
419
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);
429             return status;
430         } catch (Exception e) {
431             logger.error("Exception in addTunnelPort", e);
432             return new Status(StatusCode.INTERNALERROR);
433         }
434     }
435
436     @Override
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);
442         }
443         return new Status(StatusCode.SUCCESS);
444     }
445
446     private class NodeVlan {
447         Node node;
448         int vlan;
449         public NodeVlan(Node node, int vlan) {
450             super();
451             this.node = node;
452             this.vlan = vlan;
453         }
454         public Node getNode() {
455             return node;
456         }
457         public int getVlan() {
458             return vlan;
459         }
460         @Override
461         public String toString() {
462             return "NodeVlan [node=" + node + ", vlan=" + vlan + "]";
463         }
464         @Override
465         public int hashCode() {
466             final int prime = 31;
467             int result = 1;
468             result = prime * result + ((node == null) ? 0 : node.hashCode());
469             result = prime * result + vlan;
470             return result;
471         }
472         @Override
473         public boolean equals(Object obj) {
474             if (this == obj)
475                 return true;
476             if (obj == null)
477                 return false;
478             if (getClass() != obj.getClass())
479                 return false;
480             NodeVlan other = (NodeVlan) obj;
481             if (node == null) {
482                 if (other.node != null)
483                     return false;
484             } else if (!node.equals(other.node))
485                 return false;
486             if (vlan != other.vlan)
487                 return false;
488             return true;
489         }
490     }
491 }