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