Merge "- Addressed Queue/QoS special parent column type dependency - New files found...
[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.HashMap;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
12 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
13 import org.opendaylight.controller.sal.action.ActionType;
14 import org.opendaylight.controller.sal.core.Node;
15 import org.opendaylight.controller.sal.utils.HexEncode;
16 import org.opendaylight.controller.sal.utils.ServiceHelper;
17 import org.opendaylight.controller.sal.utils.Status;
18 import org.opendaylight.controller.sal.utils.StatusCode;
19 import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
20 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
21 import org.opendaylight.ovsdb.lib.notation.UUID;
22 import org.opendaylight.ovsdb.lib.table.Bridge;
23 import org.opendaylight.ovsdb.lib.table.Interface;
24 import org.opendaylight.ovsdb.lib.table.Port;
25 import org.opendaylight.ovsdb.lib.table.internal.Table;
26 import org.opendaylight.ovsdb.neutron.AdminConfigManager;
27 import org.opendaylight.ovsdb.neutron.InternalNetworkManager;
28 import org.opendaylight.ovsdb.neutron.TenantNetworkManager;
29 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
30 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
31 import org.opendaylight.ovsdb.plugin.StatusWithUuid;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36 class OF10ProviderManager extends ProviderNetworkManager {
37     private static final Logger logger = LoggerFactory.getLogger(OF10ProviderManager.class);
38     Map<NodeVlan, FlowConfig> floodEntries = new HashMap<NodeVlan, FlowConfig>();
39     private static final int INGRESS_TUNNEL_FLOW_PRIORITY = 100;
40     private static final int EGRESS_TUNNEL_FLOW_PRIORITY = 100;
41     private static final int FLOOD_TUNNEL_FLOW_PRIORITY = 1;
42
43     @Override
44     public boolean hasPerTenantTunneling() {
45         return true;
46     }
47
48     private Status getTunnelReadinessStatus (Node node, String tunnelKey) {
49         InetAddress srcTunnelEndPoint = AdminConfigManager.getManager().getTunnelEndPoint(node);
50         if (srcTunnelEndPoint == null) {
51             logger.error("Tunnel Endpoint not configured for Node {}", node);
52             return new Status(StatusCode.NOTFOUND, "Tunnel Endpoint not configured for "+ node);
53         }
54
55         if (!InternalNetworkManager.getManager().isInternalNetworkOverlayReady(node)) {
56             logger.error(node+" is not Overlay ready");
57             return new Status(StatusCode.NOTACCEPTABLE, node+" is not Overlay ready");
58         }
59
60         if (!TenantNetworkManager.getManager().isTenantNetworkPresentInNode(node, tunnelKey)) {
61             logger.debug(node+" has no VM corresponding to segment "+ tunnelKey);
62             return new Status(StatusCode.NOTACCEPTABLE, node+" has no VM corresponding to segment "+ tunnelKey);
63         }
64         return new Status(StatusCode.SUCCESS);
65     }
66
67     /**
68      * Program OF1.0 Flow rules on br-tun on the ingress direction from the network towards the br-int.
69      * The logic is to simply match on the incoming tunnel OF-Port (which carries the TenantNetwork GRE-Key)
70      * and rewrite the Corresponding internal Vlan and pass it on to br-int via the patch port.
71      */
72     private void programLocalIngressTunnelBridgeRules(Node node, int tunnelOFPort, int internalVlan, int patchPort) {
73         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
74         if (brIntId == null) {
75             logger.error("Failed to initialize Flow Rules for {}", node);
76             return;
77         }
78         try {
79             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan;
80             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
81             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
82             Set<String> dpids = bridge.getDatapath_id();
83             if (dpids == null || dpids.size() ==  0) return;
84             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
85             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
86             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
87                     IForwardingRulesManager.class, "default", this);
88             if (frm.getStaticFlow(flowName, ofNode) != null) {
89                 logger.debug("Local Ingress Flow exists : {} for Flow {} on {} / {}", flowName, ofNode, node);
90                 return;
91             }
92
93             FlowConfig flow = new FlowConfig();
94             flow.setName(flowName);
95             flow.setNode(ofNode);
96             flow.setPriority(INGRESS_TUNNEL_FLOW_PRIORITY+"");
97             flow.setIngressPort(tunnelOFPort+"");
98             List<String> actions = new ArrayList<String>();
99             actions.add(ActionType.SET_VLAN_ID+"="+internalVlan);
100             actions.add(ActionType.OUTPUT.toString()+"="+patchPort);
101             flow.setActions(actions);
102             Status status = frm.addStaticFlow(flow);
103             logger.debug("Local Ingress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
104         } catch (Exception e) {
105             logger.error("Failed to initialize Flow Rules for {}", node, e);
106         }
107     }
108
109     /**
110      * Program OF1.0 Flow rules on br-tun on the remote Node on its egress direction towards the overlay network
111      * for a VM (with the attachedMac).
112      * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
113      * and output the traffic to the appropriate GRE Tunnel (which carries the GRE-Key for that Tenant Network).
114      * Also perform the Strip-Vlan action.
115      */
116     private void programRemoteEgressTunnelBridgeRules(Node node, int patchPort, String attachedMac,
117             int internalVlan, int tunnelOFPort) {
118         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
119         if (brIntId == null) {
120             logger.error("Failed to initialize Flow Rules for {}", node);
121             return;
122         }
123         try {
124             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
125             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
126             Set<String> dpids = bridge.getDatapath_id();
127             if (dpids == null || dpids.size() ==  0) return;
128             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
129             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
130             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac);
131             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
132                     IForwardingRulesManager.class, "default", this);
133             if (frm.getStaticFlow(flowName, ofNode) != null) {
134                 logger.debug("Remote Egress Flow exists : {} for Flow {} on {} / {}", flowName, ofNode, node);
135                 return;
136             }
137             FlowConfig flow = new FlowConfig();
138             flow.setName(flowName);
139             flow.setNode(ofNode);
140             flow.setPriority(EGRESS_TUNNEL_FLOW_PRIORITY+"");
141             flow.setDstMac(attachedMac);
142             flow.setIngressPort(patchPort+"");
143             flow.setVlanId(internalVlan+"");
144             List<String> actions = new ArrayList<String>();
145             actions.add(ActionType.POP_VLAN.toString());
146             actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
147             flow.setActions(actions);
148             Status status = frm.addStaticFlow(flow);
149             logger.debug("Remote Egress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
150         } catch (Exception e) {
151             logger.error("Failed to initialize Flow Rules for {}", node, e);
152         }
153     }
154
155     /**
156      * Program OF1.0 Flow rules to flood the broadcast & unknown-unicast traffic over br-tun on the egress direction
157      * towards the network on all the overlay tunnels that corresponds to the tenant network.
158      * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
159      * and output the traffic to all the GRE-Tunnels for this Tenant Network (which carries the GRE-Key).
160      * Also perform the Strip-Vlan action.
161      */
162     private void programFloodEgressTunnelBridgeRules(Node node, int patchPort, int internalVlan, int tunnelOFPort) {
163         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
164         if (brIntId == null) {
165             logger.error("Failed to initialize Flow Rules for {}", node);
166             return;
167         }
168         try {
169             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
170             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
171             Set<String> dpids = bridge.getDatapath_id();
172             if (dpids == null || dpids.size() ==  0) return;
173             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
174             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
175             NodeVlan nv = new NodeVlan(ofNode, internalVlan);
176             FlowConfig existingFlowConfig = floodEntries.get(nv);
177             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
178                     IForwardingRulesManager.class, "default", this);
179             FlowConfig flow = existingFlowConfig;
180             Status status = null;
181             if (flow == null) {
182                 flow = new FlowConfig();
183                 flow.setName("TepFlood"+internalVlan);
184                 flow.setNode(ofNode);
185                 flow.setPriority(FLOOD_TUNNEL_FLOW_PRIORITY+"");
186                 flow.setIngressPort(patchPort+"");
187                 flow.setVlanId(internalVlan+"");
188                 List<String> actions = new ArrayList<String>();
189                 actions.add(ActionType.POP_VLAN.toString());
190                 actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
191                 flow.setActions(actions);
192                 status = frm.addStaticFlow(flow);
193                 logger.debug("Add Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
194                               status, flow, ofNode, node);
195             } else {
196                 flow = new FlowConfig(existingFlowConfig);
197                 List<String> actions = flow.getActions();
198                 String outputPort = ActionType.OUTPUT.toString()+"="+tunnelOFPort;
199                 if (actions != null && !actions.contains(outputPort)) {
200                     actions.add(outputPort);
201                     flow.setActions(actions);
202                 } else {
203                     return;
204                 }
205                 status = frm.modifyStaticFlow(flow);
206                 logger.debug("Modify Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
207                               status, flow, ofNode, node);
208             }
209             if (status.isSuccess()) {
210                 floodEntries.put(nv, flow);
211             }
212
213         } catch (Exception e) {
214             logger.error("Failed to initialize Flow Rules for {}", node, e);
215         }
216     }
217
218     private void programTunnelRules (String tunnelType, String segmentationId, InetAddress dst, Node node,
219                                      Interface intf, boolean local) {
220         String networkId = TenantNetworkManager.getManager().getNetworkIdForSegmentationId(segmentationId);
221         if (networkId == null) {
222             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
223             return;
224         }
225         int internalVlan = TenantNetworkManager.getManager().getInternalVlan(networkId);
226         if (internalVlan == 0) {
227             logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
228             return;
229         }
230         Map<String, String> externalIds = intf.getExternal_ids();
231         if (externalIds == null) {
232             logger.error("No external_ids seen in {}", intf);
233             return;
234         }
235
236         String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
237         if (attachedMac == null) {
238             logger.error("No AttachedMac seen in {}", intf);
239             return;
240         }
241         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
242
243         int patchOFPort = -1;
244         try {
245             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
246             Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
247             if (intfs != null) {
248                 for (Table<?> row : intfs.values()) {
249                     Interface patchIntf = (Interface)row;
250                     if (patchIntf.getName().equalsIgnoreCase(patchInt)) {
251                         Set<BigInteger> of_ports = patchIntf.getOfport();
252                         if (of_ports == null || of_ports.size() <= 0) {
253                             logger.error("Could NOT Identified Patch port {} on {}", patchInt, node);
254                             continue;
255                         }
256                         patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
257                         logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
258                         break;
259                     }
260                 }
261                 if (patchOFPort == -1) {
262                     logger.error("Cannot identify {} interface on {}", patchInt, node);
263                 }
264                 for (Table<?> row : intfs.values()) {
265                     Interface tunIntf = (Interface)row;
266                     if (tunIntf.getName().equals(this.getTunnelName(tunnelType, segmentationId, dst))) {
267                         Set<BigInteger> of_ports = tunIntf.getOfport();
268                         if (of_ports == null || of_ports.size() <= 0) {
269                             logger.error("Could NOT Identify Tunnel port {} on {}", tunIntf.getName(), node);
270                             continue;
271                         }
272                         int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
273
274                         if (tunnelOFPort == -1) {
275                             logger.error("Could NOT Identify Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
276                             return;
277                         }
278                         logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
279
280                         if (!local) {
281                             programRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
282                         }
283                         programLocalIngressTunnelBridgeRules(node, tunnelOFPort, internalVlan, patchOFPort);
284                         programFloodEgressTunnelBridgeRules(node, patchOFPort, internalVlan, tunnelOFPort);
285                         return;
286                     }
287                 }
288             }
289         } catch (Exception e) {
290             logger.error("", e);
291         }
292     }
293
294     @Override
295     public Status createTunnels(String tunnelType, String tunnelKey, Node srcNode, Interface intf) {
296         Status status = getTunnelReadinessStatus(srcNode, tunnelKey);
297         if (!status.isSuccess()) return status;
298
299         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
300         List<Node> nodes = connectionService.getNodes();
301         nodes.remove(srcNode);
302         for (Node dstNode : nodes) {
303             status = getTunnelReadinessStatus(dstNode, tunnelKey);
304             if (!status.isSuccess()) continue;
305             InetAddress src = AdminConfigManager.getManager().getTunnelEndPoint(srcNode);
306             InetAddress dst = AdminConfigManager.getManager().getTunnelEndPoint(dstNode);
307             status = addTunnelPort(srcNode, tunnelType, src, dst, tunnelKey);
308             if (status.isSuccess()) {
309                 this.programTunnelRules(tunnelType, tunnelKey, dst, srcNode, intf, true);
310             }
311             addTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
312             if (status.isSuccess()) {
313                 this.programTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
314             }
315         }
316         return new Status(StatusCode.SUCCESS);
317     }
318
319     private String getTunnelName(String tunnelType, String key, InetAddress dst) {
320         return tunnelType+"-"+key+"-"+dst.getHostAddress();
321     }
322
323     private Interface getTunnelInterface (Node node, String tunnelType, InetAddress dst, String key) {
324         try {
325             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
326             String portName = getTunnelName(tunnelType, key, dst);
327
328             Map<String, Table<?>> tunIntfs = ovsdbTable.getRows(node, Interface.NAME.getName());
329             if (tunIntfs != null) {
330                 for (Table<?> row : tunIntfs.values()) {
331                     Interface tunIntf = (Interface)row;
332                     if (tunIntf.getName().equals(portName)) return tunIntf;
333                 }
334
335             }
336         } catch (Exception e) {
337             logger.error("", e);
338         }
339         return null;
340     }
341
342     private boolean isTunnelPresent(Node node, String tunnelName, String bridgeUUID) throws Exception {
343         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
344         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
345         if (bridge != null) {
346             Set<UUID> ports = bridge.getPorts();
347             for (UUID portUUID : ports) {
348                 Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID.toString());
349                 if (port != null && port.getName().equalsIgnoreCase(tunnelName)) return true;
350             }
351         }
352         return false;
353     }
354
355     private Status addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
356         try {
357             String bridgeUUID = null;
358             String tunnelBridgeName = AdminConfigManager.getManager().getTunnelBridgeName();
359             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
360             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
361             if (bridgeTable != null) {
362                 for (String uuid : bridgeTable.keySet()) {
363                     Bridge bridge = (Bridge)bridgeTable.get(uuid);
364                     if (bridge.getName().equals(tunnelBridgeName)) {
365                         bridgeUUID = uuid;
366                         break;
367                     }
368                 }
369             }
370             if (bridgeUUID == null) {
371                 logger.error("Could not find Bridge {} in {}", tunnelBridgeName, node);
372                 return new Status(StatusCode.NOTFOUND, "Could not find "+tunnelBridgeName+" in "+node);
373             }
374             String portName = getTunnelName(tunnelType, key, dst);
375
376             if (this.isTunnelPresent(node, portName, bridgeUUID)) {
377                 logger.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node);
378                 return new Status(StatusCode.SUCCESS);
379             }
380
381             Port tunnelPort = new Port();
382             tunnelPort.setName(portName);
383             StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, tunnelPort);
384             if (!statusWithUuid.isSuccess()) {
385                 logger.error("Failed to insert Tunnel port {} in {}", portName, bridgeUUID);
386                 return statusWithUuid;
387             }
388
389             String tunnelPortUUID = statusWithUuid.getUuid().toString();
390             String interfaceUUID = null;
391             int timeout = 6;
392             while ((interfaceUUID == null) && (timeout > 0)) {
393                 tunnelPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), tunnelPortUUID);
394                 OvsDBSet<UUID> interfaces = tunnelPort.getInterfaces();
395                 if (interfaces == null || interfaces.size() == 0) {
396                     // Wait for the OVSDB update to sync up the Local cache.
397                     Thread.sleep(500);
398                     timeout--;
399                     continue;
400                 }
401                 interfaceUUID = interfaces.toArray()[0].toString();
402                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
403                 if (intf == null) interfaceUUID = null;
404             }
405
406             if (interfaceUUID == null) {
407                 logger.error("Cannot identify Tunnel Interface for port {}/{}", portName, tunnelPortUUID);
408                 return new Status(StatusCode.INTERNALERROR);
409             }
410
411             Interface tunInterface = new Interface();
412             tunInterface.setType(tunnelType);
413             OvsDBMap<String, String> options = new OvsDBMap<String, String>();
414             options.put("key", key);
415             options.put("local_ip", src.getHostAddress());
416             options.put("remote_ip", dst.getHostAddress());
417             tunInterface.setOptions(options);
418             Status status = ovsdbTable.updateRow(node, Interface.NAME.getName(), tunnelPortUUID, interfaceUUID, tunInterface);
419             logger.debug("Tunnel {} add status : {}", tunInterface, status);
420             return status;
421         } catch (Exception e) {
422             logger.error("Exception in addTunnelPort", e);
423             return new Status(StatusCode.INTERNALERROR);
424         }
425     }
426
427     @Override
428     public Status createTunnels(String tunnelType, String tunnelKey) {
429         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
430         List<Node> nodes = connectionService.getNodes();
431         for (Node srcNode : nodes) {
432             this.createTunnels(tunnelType, tunnelKey, srcNode, null);
433         }
434         return new Status(StatusCode.SUCCESS);
435     }
436
437     private class NodeVlan {
438         Node node;
439         int vlan;
440         public NodeVlan(Node node, int vlan) {
441             super();
442             this.node = node;
443             this.vlan = vlan;
444         }
445         public Node getNode() {
446             return node;
447         }
448         public int getVlan() {
449             return vlan;
450         }
451         @Override
452         public String toString() {
453             return "NodeVlan [node=" + node + ", vlan=" + vlan + "]";
454         }
455         @Override
456         public int hashCode() {
457             final int prime = 31;
458             int result = 1;
459             result = prime * result + ((node == null) ? 0 : node.hashCode());
460             result = prime * result + vlan;
461             return result;
462         }
463         @Override
464         public boolean equals(Object obj) {
465             if (this == obj)
466                 return true;
467             if (obj == null)
468                 return false;
469             if (getClass() != obj.getClass())
470                 return false;
471             NodeVlan other = (NodeVlan) obj;
472             if (node == null) {
473                 if (other.node != null)
474                     return false;
475             } else if (!node.equals(other.node))
476                 return false;
477             if (vlan != other.vlan)
478                 return false;
479             return true;
480         }
481     }
482 }