Removed the ovsdb subdirectory properly in lieu of the upcoming rebase with master
[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.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
20 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
21 import org.opendaylight.controller.sal.action.ActionType;
22 import org.opendaylight.controller.sal.core.Node;
23 import org.opendaylight.controller.sal.utils.EtherTypes;
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.controller.switchmanager.ISwitchManager;
29 import org.opendaylight.controller.switchmanager.Switch;
30 import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
31 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
32 import org.opendaylight.ovsdb.lib.notation.UUID;
33 import org.opendaylight.ovsdb.lib.table.Bridge;
34 import org.opendaylight.ovsdb.lib.table.Interface;
35 import org.opendaylight.ovsdb.lib.table.Port;
36 import org.opendaylight.ovsdb.lib.table.Table;
37 import org.opendaylight.ovsdb.neutron.AdminConfigManager;
38 import org.opendaylight.ovsdb.neutron.InternalNetworkManager;
39 import org.opendaylight.ovsdb.neutron.TenantNetworkManager;
40 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
41 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
42 import org.opendaylight.ovsdb.plugin.StatusWithUuid;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47 class OF10ProviderManager extends ProviderNetworkManager {
48     private static final Logger logger = LoggerFactory.getLogger(OF10ProviderManager.class);
49     private static final int INGRESS_TUNNEL_FLOW_PRIORITY = 100;
50     private static final int EGRESS_TUNNEL_FLOW_PRIORITY = 100;
51     private static final int FLOOD_TUNNEL_FLOW_PRIORITY = 1;
52
53     @Override
54     public boolean hasPerTenantTunneling() {
55         return true;
56     }
57
58     private Status getTunnelReadinessStatus (Node node, String tunnelKey) {
59         InetAddress srcTunnelEndPoint = AdminConfigManager.getManager().getTunnelEndPoint(node);
60         if (srcTunnelEndPoint == null) {
61             logger.error("Tunnel Endpoint not configured for Node {}", node);
62             return new Status(StatusCode.NOTFOUND, "Tunnel Endpoint not configured for "+ node);
63         }
64
65         if (!InternalNetworkManager.getManager().isInternalNetworkOverlayReady(node)) {
66             logger.warn("{} is not Overlay ready. It might be an OpenStack Controller Node", node);
67             return new Status(StatusCode.NOTACCEPTABLE, node+" is not Overlay ready");
68         }
69
70         if (!TenantNetworkManager.getManager().isTenantNetworkPresentInNode(node, tunnelKey)) {
71             logger.debug(node+" has no VM corresponding to segment "+ tunnelKey);
72             return new Status(StatusCode.NOTACCEPTABLE, node+" has no VM corresponding to segment "+ tunnelKey);
73         }
74         return new Status(StatusCode.SUCCESS);
75     }
76
77     /**
78      * Program OF1.0 Flow rules on br-tun on the ingress direction from the network towards the br-int.
79      * The logic is to simply match on the incoming tunnel OF-Port (which carries the TenantNetwork GRE-Key)
80      * and rewrite the Corresponding internal Vlan and pass it on to br-int via the patch port.
81      */
82     private void programLocalIngressTunnelBridgeRules(Node node, int tunnelOFPort, int internalVlan, int patchPort) {
83         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
84         if (brIntId == null) {
85             logger.error("Failed to initialize Flow Rules for {}", node);
86             return;
87         }
88         try {
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             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan;
96             FlowConfig flow = new FlowConfig();
97             flow.setName(flowName);
98             flow.setNode(ofNode);
99             flow.setInstallInHw(true);
100             flow.setPriority(INGRESS_TUNNEL_FLOW_PRIORITY+"");
101             flow.setIngressPort(tunnelOFPort+"");
102             List<String> actions = new ArrayList<String>();
103             actions.add(ActionType.SET_VLAN_ID+"="+internalVlan);
104             actions.add(ActionType.OUTPUT.toString()+"="+patchPort);
105             flow.setActions(actions);
106             Status status = this.addStaticFlow(ofNode, flow);
107             logger.debug("Local Ingress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
108         } catch (Exception e) {
109             logger.error("Failed to initialize Flow Rules for {}", node, e);
110         }
111     }
112
113     private void removeLocalIngressTunnelBridgeRules(Node node, int tunnelOFPort, int internalVlan, int patchPort) {
114         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
115         if (brIntId == null) {
116             logger.error("Failed to remove Flow Rules for {}", node);
117             return;
118         }
119         try {
120             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
121             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
122             Set<String> dpids = bridge.getDatapath_id();
123             if (dpids == null || dpids.size() ==  0) return;
124             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
125             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
126             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan;
127
128             Status status = this.deleteStaticFlow(ofNode, flowName);
129             logger.debug("Local Ingress Flow Removal Status {} for Flow {} on {} / {}", status, flowName, ofNode, node);
130         } catch (Exception e) {
131             logger.error("Failed to Remove Flow Rules for {}", node, e);
132         }
133     }
134
135     /**
136      * Program OF1.0 Flow rules on br-tun on the remote Node on its egress direction towards the overlay network
137      * for a VM (with the attachedMac).
138      * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
139      * and output the traffic to the appropriate GRE Tunnel (which carries the GRE-Key for that Tenant Network).
140      * Also perform the Strip-Vlan action.
141      */
142     private void programRemoteEgressTunnelBridgeRules(Node node, int patchPort, String attachedMac,
143             int internalVlan, int tunnelOFPort) {
144         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
145         if (brIntId == null) {
146             logger.error("Failed to initialize Flow Rules for {}", node);
147             return;
148         }
149         try {
150             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
151             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
152             Set<String> dpids = bridge.getDatapath_id();
153             if (dpids == null || dpids.size() ==  0) return;
154             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
155             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
156             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac);
157             FlowConfig flow = new FlowConfig();
158             flow.setName(flowName);
159             flow.setNode(ofNode);
160             flow.setInstallInHw(true);
161             flow.setPriority(EGRESS_TUNNEL_FLOW_PRIORITY+"");
162             flow.setDstMac(attachedMac);
163             flow.setIngressPort(patchPort+"");
164             flow.setVlanId(internalVlan+"");
165             List<String> actions = new ArrayList<String>();
166             actions.add(ActionType.POP_VLAN.toString());
167             actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
168             flow.setActions(actions);
169             Status status = this.addStaticFlow(ofNode, flow);
170             logger.debug("Remote Egress Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
171         } catch (Exception e) {
172             logger.error("Failed to initialize Flow Rules for {}", node, e);
173         }
174     }
175
176     private void removeRemoteEgressTunnelBridgeRules(Node node, int patchPort, String attachedMac,
177             int internalVlan, int tunnelOFPort) {
178         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
179         if (brIntId == null) {
180             logger.error("Failed to initialize Flow Rules for {}", node);
181             return;
182         }
183         try {
184             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
185             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
186             Set<String> dpids = bridge.getDatapath_id();
187             if (dpids == null || dpids.size() ==  0) return;
188             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
189             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
190             String flowName = "TepMatch"+tunnelOFPort+""+internalVlan+""+HexEncode.stringToLong(attachedMac);
191             Status status = this.deleteStaticFlow(ofNode, flowName);
192             logger.debug("Remote Egress Flow Removal Status {} for Flow {} on {} / {}", status, flowName, ofNode, node);
193         } catch (Exception e) {
194             logger.error("Failed to Remove Flow Rules for {}", node, e);
195         }
196     }
197
198     /**
199      * Program OF1.0 Flow rules to flood the broadcast & unknown-unicast traffic over br-tun on the egress direction
200      * towards the network on all the overlay tunnels that corresponds to the tenant network.
201      * The logic is to simply match on the incoming vlan, mac from the patch-port connected to br-int (patch-int)
202      * and output the traffic to all the GRE-Tunnels for this Tenant Network (which carries the GRE-Key).
203      * Also perform the Strip-Vlan action.
204      */
205     private void programFloodEgressTunnelBridgeRules(Node node, int patchPort, int internalVlan, int tunnelOFPort) {
206         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
207         if (brIntId == null) {
208             logger.error("Failed to initialize Flow Rules for {}", node);
209             return;
210         }
211         try {
212             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
213             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
214             Set<String> dpids = bridge.getDatapath_id();
215             if (dpids == null || dpids.size() ==  0) return;
216             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
217             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
218             String flowName = "TepFlood"+internalVlan;
219             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
220                     IForwardingRulesManager.class, "default", this);
221             FlowConfig existingFlowConfig = frm.getStaticFlow(flowName, ofNode);
222             FlowConfig flow = existingFlowConfig;
223             Status status = null;
224             if (flow == null) {
225                 flow = new FlowConfig();
226                 flow.setName(flowName);
227                 flow.setNode(ofNode);
228                 flow.setPriority(FLOOD_TUNNEL_FLOW_PRIORITY+"");
229                 flow.setIngressPort(patchPort+"");
230                 flow.setVlanId(internalVlan+"");
231                 List<String> actions = new ArrayList<String>();
232                 actions.add(ActionType.POP_VLAN.toString());
233                 actions.add(ActionType.OUTPUT.toString()+"="+tunnelOFPort);
234                 flow.setActions(actions);
235                 status = frm.addStaticFlow(flow);
236                 logger.debug("Add Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
237                               status, flow, ofNode, node);
238             } else {
239                 flow = new FlowConfig(existingFlowConfig);
240                 List<String> actions = flow.getActions();
241                 String outputPort = ActionType.OUTPUT.toString()+"="+tunnelOFPort;
242                 if (actions != null && !actions.contains(outputPort)) {
243                     actions.add(outputPort);
244                     flow.setActions(actions);
245                 } else {
246                     logger.debug("Flood Egress Flow already exists. Skipping modify for Flow {} on {} / {}",
247                                  flow, ofNode, node);
248                     return;
249                 }
250                 status = frm.modifyStaticFlow(flow);
251                 logger.debug("Modify Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
252                               status, flow, ofNode, node);
253             }
254         } catch (Exception e) {
255             logger.error("Failed to initialize Flow Rules for {}", node, e);
256         }
257     }
258
259     private void removeFloodEgressTunnelBridgeRules(Node node, int patchPort, int internalVlan, int tunnelOFPort) {
260         String brIntId = InternalNetworkManager.getManager().getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName());
261         if (brIntId == null) {
262             logger.error("Failed to remove Flow Rules for {}", node);
263             return;
264         }
265         try {
266             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
267             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
268             Set<String> dpids = bridge.getDatapath_id();
269             if (dpids == null || dpids.size() ==  0) return;
270             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
271             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
272             String flowName = "TepFlood"+internalVlan;
273             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
274                     IForwardingRulesManager.class, "default", this);
275             FlowConfig flow = frm.getStaticFlow(flowName, ofNode);
276             Status status = null;
277             if (flow != null) {
278                 status = frm.removeStaticFlow(flowName, ofNode);
279                 logger.debug("Remove Flood Egress Flow Programming Status {} for Flow {} on {} / {}",
280                               status, flow, ofNode, node);
281
282             } else {
283                 logger.debug("Flood Egress Flow already removed. Skipping removal for Flow {} on {} / {}",
284                              flow, ofNode, node);
285                 return;
286             }
287         } catch (Exception e) {
288             logger.error("Failed to remove Flow Rules for {}", node, e);
289         }
290     }
291
292     private void programTunnelRules (String tunnelType, String segmentationId, InetAddress dst, Node node,
293                                      Interface intf, boolean local) {
294         String networkId = TenantNetworkManager.getManager().getNetworkIdForSegmentationId(segmentationId);
295         if (networkId == null) {
296             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
297             return;
298         }
299         int internalVlan = TenantNetworkManager.getManager().getInternalVlan(node, networkId);
300         if (internalVlan == 0) {
301             logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
302             return;
303         }
304         Map<String, String> externalIds = intf.getExternal_ids();
305         if (externalIds == null) {
306             logger.error("No external_ids seen in {}", intf);
307             return;
308         }
309
310         String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
311         if (attachedMac == null) {
312             logger.error("No AttachedMac seen in {}", intf);
313             return;
314         }
315         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
316
317         int patchOFPort = -1;
318         try {
319             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
320             Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
321             if (intfs != null) {
322                 for (Table<?> row : intfs.values()) {
323                     Interface patchIntf = (Interface)row;
324                     if (patchIntf.getName().equalsIgnoreCase(patchInt)) {
325                         Set<BigInteger> of_ports = patchIntf.getOfport();
326                         if (of_ports == null || of_ports.size() <= 0) {
327                             logger.error("Could NOT Identified Patch port {} on {}", patchInt, node);
328                             continue;
329                         }
330                         patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
331                         logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
332                         break;
333                     }
334                 }
335                 if (patchOFPort == -1) {
336                     logger.error("Cannot identify {} interface on {}", patchInt, node);
337                 }
338                 for (Table<?> row : intfs.values()) {
339                     Interface tunIntf = (Interface)row;
340                     if (tunIntf.getName().equals(this.getTunnelName(tunnelType, segmentationId, dst))) {
341                         Set<BigInteger> of_ports = tunIntf.getOfport();
342                         if (of_ports == null || of_ports.size() <= 0) {
343                             logger.warn("Could not Identify Tunnel port {} on {}. Don't panic. It might get converged soon...", tunIntf.getName(), node);
344                             continue;
345                         }
346                         int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
347
348                         if (tunnelOFPort == -1) {
349                             logger.warn("Tunnel Port {} on node {}: OFPort = -1 . Don't panic. It might get converged soon...", tunIntf.getName(), node);
350                             return;
351                         }
352                         logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
353
354                         if (!local) {
355                             programRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
356                         }
357                         programLocalIngressTunnelBridgeRules(node, tunnelOFPort, internalVlan, patchOFPort);
358                         programFloodEgressTunnelBridgeRules(node, patchOFPort, internalVlan, tunnelOFPort);
359                         return;
360                     }
361                 }
362             }
363         } catch (Exception e) {
364             logger.error("", e);
365         }
366     }
367
368     private void removeTunnelRules (String tunnelType, String segmentationId, InetAddress dst, Node node,
369             Interface intf, boolean local) {
370         String networkId = TenantNetworkManager.getManager().getNetworkIdForSegmentationId(segmentationId);
371         if (networkId == null) {
372             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
373             return;
374         }
375         int internalVlan = TenantNetworkManager.getManager().getInternalVlan(node,networkId);
376         if (internalVlan == 0) {
377             logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
378             return;
379         }
380         Map<String, String> externalIds = intf.getExternal_ids();
381         if (externalIds == null) {
382             logger.error("No external_ids seen in {}", intf);
383             return;
384         }
385
386         String attachedMac = externalIds.get(TenantNetworkManager.EXTERNAL_ID_VM_MAC);
387         if (attachedMac == null) {
388             logger.error("No AttachedMac seen in {}", intf);
389             return;
390         }
391         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
392
393         int patchOFPort = -1;
394         try {
395             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
396             Map<String, Table<?>> intfs = ovsdbTable.getRows(node, Interface.NAME.getName());
397             if (intfs != null) {
398                 for (Table<?> row : intfs.values()) {
399                     Interface patchIntf = (Interface)row;
400                     if (patchIntf.getName().equalsIgnoreCase(patchInt)) {
401                         Set<BigInteger> of_ports = patchIntf.getOfport();
402                         if (of_ports == null || of_ports.size() <= 0) {
403                             logger.error("Could NOT Identified Patch port {} on {}", patchInt, node);
404                             continue;
405                         }
406                         patchOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
407                         logger.debug("Identified Patch port {} -> OF ({}) on {}", patchInt, patchOFPort, node);
408                         break;
409                     }
410                 }
411                 if (patchOFPort == -1) {
412                     logger.error("Cannot identify {} interface on {}", patchInt, node);
413                 }
414                 for (Table<?> row : intfs.values()) {
415                     Interface tunIntf = (Interface)row;
416                     if (tunIntf.getName().equals(this.getTunnelName(tunnelType, segmentationId, dst))) {
417                         Set<BigInteger> of_ports = tunIntf.getOfport();
418                         if (of_ports == null || of_ports.size() <= 0) {
419                             logger.error("Could NOT Identify Tunnel port {} on {}", tunIntf.getName(), node);
420                             continue;
421                         }
422                         int tunnelOFPort = Long.valueOf(((BigInteger)of_ports.toArray()[0]).longValue()).intValue();
423
424                         if (tunnelOFPort == -1) {
425                             logger.error("Could NOT Identify Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
426                             return;
427                         }
428                         logger.debug("Identified Tunnel port {} -> OF ({}) on {}", tunIntf.getName(), tunnelOFPort, node);
429
430                         if (!local) {
431                             removeRemoteEgressTunnelBridgeRules(node, patchOFPort, attachedMac, internalVlan, tunnelOFPort);
432                         }
433                         removeLocalIngressTunnelBridgeRules(node, tunnelOFPort, internalVlan, patchOFPort);
434                         removeFloodEgressTunnelBridgeRules(node, patchOFPort, internalVlan, tunnelOFPort);
435                         return;
436                     }
437                 }
438             }
439         } catch (Exception e) {
440             logger.error("", e);
441         }
442     }
443
444     @Override
445     public Status handleInterfaceUpdate(String tunnelType, String tunnelKey, Node srcNode, Interface intf) {
446         Status status = getTunnelReadinessStatus(srcNode, tunnelKey);
447         if (!status.isSuccess()) return status;
448
449         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
450         List<Node> nodes = connectionService.getNodes();
451         nodes.remove(srcNode);
452         for (Node dstNode : nodes) {
453             status = getTunnelReadinessStatus(dstNode, tunnelKey);
454             if (!status.isSuccess()) continue;
455             InetAddress src = AdminConfigManager.getManager().getTunnelEndPoint(srcNode);
456             InetAddress dst = AdminConfigManager.getManager().getTunnelEndPoint(dstNode);
457             status = addTunnelPort(srcNode, tunnelType, src, dst, tunnelKey);
458             if (status.isSuccess()) {
459                 this.programTunnelRules(tunnelType, tunnelKey, dst, srcNode, intf, true);
460             }
461             addTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
462             if (status.isSuccess()) {
463                 this.programTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
464             }
465         }
466         return new Status(StatusCode.SUCCESS);
467     }
468
469     @Override
470     public Status handleInterfaceDelete(String tunnelType, String tunnelKey, Node srcNode, Interface intf, boolean isLastInstanceOnNode) {
471         Status status = new Status(StatusCode.SUCCESS);
472
473         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
474         List<Node> nodes = connectionService.getNodes();
475         nodes.remove(srcNode);
476         for (Node dstNode : nodes) {
477             InetAddress src = AdminConfigManager.getManager().getTunnelEndPoint(srcNode);
478             InetAddress dst = AdminConfigManager.getManager().getTunnelEndPoint(dstNode);
479             this.removeTunnelRules(tunnelType, tunnelKey, dst, srcNode, intf, true);
480             if (isLastInstanceOnNode) {
481                 status = deleteTunnelPort(srcNode, tunnelType, src, dst, tunnelKey);
482             }
483             this.removeTunnelRules(tunnelType, tunnelKey, src, dstNode, intf, false);
484             if (status.isSuccess() && isLastInstanceOnNode) {
485                 deleteTunnelPort(dstNode, tunnelType, dst, src, tunnelKey);
486             }
487         }
488         return status;
489     }
490
491     private String getTunnelName(String tunnelType, String key, InetAddress dst) {
492         return tunnelType+"-"+key+"-"+dst.getHostAddress();
493     }
494
495     private Interface getTunnelInterface (Node node, String tunnelType, InetAddress dst, String key) {
496         try {
497             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
498             String portName = getTunnelName(tunnelType, key, dst);
499
500             Map<String, Table<?>> tunIntfs = ovsdbTable.getRows(node, Interface.NAME.getName());
501             if (tunIntfs != null) {
502                 for (Table<?> row : tunIntfs.values()) {
503                     Interface tunIntf = (Interface)row;
504                     if (tunIntf.getName().equals(portName)) return tunIntf;
505                 }
506
507             }
508         } catch (Exception e) {
509             logger.error("", e);
510         }
511         return null;
512     }
513
514     private boolean isTunnelPresent(Node node, String tunnelName, String bridgeUUID) throws Exception {
515         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
516         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
517         if (bridge != null) {
518             Set<UUID> ports = bridge.getPorts();
519             for (UUID portUUID : ports) {
520                 Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID.toString());
521                 if (port != null && port.getName().equalsIgnoreCase(tunnelName)) return true;
522             }
523         }
524         return false;
525     }
526
527     private String getTunnelPortUuid(Node node, String tunnelName, String bridgeUUID) throws Exception {
528         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
529         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
530         if (bridge != null) {
531             Set<UUID> ports = bridge.getPorts();
532             for (UUID portUUID : ports) {
533                 Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID.toString());
534                 if (port != null && port.getName().equalsIgnoreCase(tunnelName)) return portUUID.toString();
535             }
536         }
537         return null;
538     }
539
540     private Status addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
541         try {
542             String bridgeUUID = null;
543             String tunnelBridgeName = AdminConfigManager.getManager().getTunnelBridgeName();
544             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
545             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
546             if (bridgeTable != null) {
547                 for (String uuid : bridgeTable.keySet()) {
548                     Bridge bridge = (Bridge)bridgeTable.get(uuid);
549                     if (bridge.getName().equals(tunnelBridgeName)) {
550                         bridgeUUID = uuid;
551                         break;
552                     }
553                 }
554             }
555             if (bridgeUUID == null) {
556                 logger.error("Could not find Bridge {} in {}", tunnelBridgeName, node);
557                 return new Status(StatusCode.NOTFOUND, "Could not find "+tunnelBridgeName+" in "+node);
558             }
559             String portName = getTunnelName(tunnelType, key, dst);
560
561             if (this.isTunnelPresent(node, portName, bridgeUUID)) {
562                 logger.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node);
563                 return new Status(StatusCode.SUCCESS);
564             }
565
566             Port tunnelPort = new Port();
567             tunnelPort.setName(portName);
568             StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, tunnelPort);
569             if (!statusWithUuid.isSuccess()) {
570                 logger.error("Failed to insert Tunnel port {} in {}", portName, bridgeUUID);
571                 return statusWithUuid;
572             }
573
574             String tunnelPortUUID = statusWithUuid.getUuid().toString();
575             String interfaceUUID = null;
576             int timeout = 6;
577             while ((interfaceUUID == null) && (timeout > 0)) {
578                 tunnelPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), tunnelPortUUID);
579                 OvsDBSet<UUID> interfaces = tunnelPort.getInterfaces();
580                 if (interfaces == null || interfaces.size() == 0) {
581                     // Wait for the OVSDB update to sync up the Local cache.
582                     Thread.sleep(500);
583                     timeout--;
584                     continue;
585                 }
586                 interfaceUUID = interfaces.toArray()[0].toString();
587                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
588                 if (intf == null) interfaceUUID = null;
589             }
590
591             if (interfaceUUID == null) {
592                 logger.error("Cannot identify Tunnel Interface for port {}/{}", portName, tunnelPortUUID);
593                 return new Status(StatusCode.INTERNALERROR);
594             }
595
596             Interface tunInterface = new Interface();
597             tunInterface.setType(tunnelType);
598             OvsDBMap<String, String> options = new OvsDBMap<String, String>();
599             options.put("key", key);
600             options.put("local_ip", src.getHostAddress());
601             options.put("remote_ip", dst.getHostAddress());
602             tunInterface.setOptions(options);
603             Status status = ovsdbTable.updateRow(node, Interface.NAME.getName(), tunnelPortUUID, interfaceUUID, tunInterface);
604             logger.debug("Tunnel {} add status : {}", tunInterface, status);
605             return status;
606         } catch (Exception e) {
607             logger.error("Exception in addTunnelPort", e);
608             return new Status(StatusCode.INTERNALERROR);
609         }
610     }
611
612     private Status deleteTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst, String key) {
613         try {
614             String bridgeUUID = null;
615             String tunnelBridgeName = AdminConfigManager.getManager().getTunnelBridgeName();
616             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
617             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
618             if (bridgeTable != null) {
619                 for (String uuid : bridgeTable.keySet()) {
620                     Bridge bridge = (Bridge)bridgeTable.get(uuid);
621                     if (bridge.getName().equals(tunnelBridgeName)) {
622                         bridgeUUID = uuid;
623                         break;
624                     }
625                 }
626             }
627             if (bridgeUUID == null) {
628                 logger.debug("Could not find Bridge {} in {}", tunnelBridgeName, node);
629                 return new Status(StatusCode.SUCCESS);
630             }
631             String portName = getTunnelName(tunnelType, key, dst);
632             String tunnelPortUUID = this.getTunnelPortUuid(node, portName, bridgeUUID);
633             Status status = ovsdbTable.deleteRow(node, Port.NAME.getName(), tunnelPortUUID);
634             if (!status.isSuccess()) {
635                 logger.error("Failed to delete Tunnel port {} in {} status : {}", portName, bridgeUUID, status);
636                 return status;
637             }
638
639             logger.debug("Tunnel {} delete status : {}", portName, status);
640             return status;
641         } catch (Exception e) {
642             logger.error("Exception in deleteTunnelPort", e);
643             return new Status(StatusCode.INTERNALERROR);
644         }
645     }
646
647     @Override
648     public Status handleInterfaceUpdate(String tunnelType, String tunnelKey) {
649         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
650         List<Node> nodes = connectionService.getNodes();
651         for (Node srcNode : nodes) {
652             this.handleInterfaceUpdate(tunnelType, tunnelKey, srcNode, null);
653         }
654         return new Status(StatusCode.SUCCESS);
655     }
656
657     @Override
658     public void initializeFlowRules(Node node) {
659         this.initializeFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
660         this.initializeFlowRules(node, AdminConfigManager.getManager().getTunnelBridgeName());
661         this.initializeFlowRules(node, AdminConfigManager.getManager().getExternalBridgeName());
662     }
663
664     private void initializeFlowRules(Node node, String bridgeName) {
665         String brIntId = this.getInternalBridgeUUID(node, bridgeName);
666         if (brIntId == null) {
667             if (bridgeName == AdminConfigManager.getManager().getExternalBridgeName()){
668                 logger.debug("Failed to initialize Flow Rules for bridge {} on node {}. Is the Neutron L3 agent running on this node?");
669             }
670             else {
671                 logger.debug("Failed to initialize Flow Rules for bridge {} on node {}", bridgeName, node);
672             }
673             return;
674         }
675
676         try {
677             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
678             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
679             Set<String> dpids = bridge.getDatapath_id();
680             if (dpids == null || dpids.size() ==  0) return;
681             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
682             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
683             ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, "default", this);
684             List<Switch> nodes = switchManager.getNetworkDevices();
685             if (nodes == null) {
686                 logger.debug("No OF nodes learned yet in {}", node);
687                 return;
688             }
689             for (Switch device : nodes) {
690                 if (device.getNode().equals(ofNode)) {
691                     logger.debug("Initialize OF Flows on {}", ofNode);
692                     initializeNormalFlowRules(ofNode);
693                     return;
694                 }
695             }
696             logger.debug("Could not identify OF node {} for bridge {} in {}", ofNode.toString(), bridgeName, node.toString());
697         } catch (Exception e) {
698             logger.error("Failed to initialize Flow Rules for "+node.toString(), e);
699         }
700     }
701
702     @Override
703     public void initializeOFFlowRules(Node openflowNode) {
704         this.initializeNormalFlowRules(openflowNode);
705         this.initializeLLDPFlowRules(openflowNode);
706     }
707
708     private void initializeNormalFlowRules(Node ofNode) {
709         String flowName = ActionType.HW_PATH.toString();
710         FlowConfig flow = new FlowConfig();
711         flow.setName("NORMAL");
712         flow.setNode(ofNode);
713         flow.setPriority(NORMAL_PRIORITY+"");
714         flow.setInstallInHw(true);
715         List<String> normalAction = new ArrayList<String>();
716         normalAction.add(flowName);
717         flow.setActions(normalAction);
718         Status status = this.addStaticFlow(ofNode, flow);
719         logger.debug("Flow Programming Add Status {} for Flow {} on {}", status, flow, ofNode);
720     }
721
722     private void initializeLLDPFlowRules(Node ofNode) {
723         String flowName = "PuntLLDP";
724         List<String> puntAction = new ArrayList<String>();
725         puntAction.add(ActionType.CONTROLLER.toString());
726
727         FlowConfig allowLLDP = new FlowConfig();
728         allowLLDP.setName(flowName);
729         allowLLDP.setPriority(LLDP_PRIORITY+"");
730         allowLLDP.setNode(ofNode);
731         allowLLDP.setInstallInHw(true);
732         allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue()).toUpperCase());
733         allowLLDP.setActions(puntAction);
734         Status status = this.addStaticFlow(ofNode, allowLLDP);
735         logger.debug("LLDP Flow Add Status {} for Flow {} on {}", status, allowLLDP, ofNode);
736     }
737
738     private Status addStaticFlow (Node ofNode, FlowConfig flowConfig) {
739         IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
740                 IForwardingRulesManager.class, "default", this);
741         String flowName = flowConfig.getName();
742         if (frm.getStaticFlow(flowName, ofNode) != null) {
743             logger.debug("Flow already exists {} on {}. Skipping installation.", flowName, ofNode);
744             return new Status(StatusCode.CONFLICT, "Flow with name "+flowName+" exists in node "+ofNode.toString());
745         }
746         return frm.addStaticFlow(flowConfig);
747     }
748
749     private Status deleteStaticFlow (Node ofNode, String flowName) {
750         IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
751                 IForwardingRulesManager.class, "default", this);
752         if (frm.getStaticFlow(flowName, ofNode) == null) {
753             logger.debug("Flow doese not exist {} on {}. Skipping deletion.", flowName, ofNode);
754             return new Status(StatusCode.SUCCESS);
755         }
756         return frm.removeStaticFlow(flowName,ofNode);
757     }}