Merge "Disabling the installation of LLDP Punt rules to Controller."
[netvirt.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / InternalNetworkManager.java
1 package org.opendaylight.ovsdb.neutron;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8
9 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
10 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
11 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
12 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
13 import org.opendaylight.controller.sal.action.ActionType;
14 import org.opendaylight.controller.sal.core.Node;
15 import org.opendaylight.controller.sal.utils.EtherTypes;
16 import org.opendaylight.controller.sal.utils.HexEncode;
17 import org.opendaylight.controller.sal.utils.ServiceHelper;
18 import org.opendaylight.controller.sal.utils.Status;
19 import org.opendaylight.controller.sal.utils.StatusCode;
20 import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
21 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
22 import org.opendaylight.ovsdb.lib.notation.UUID;
23 import org.opendaylight.ovsdb.lib.table.Bridge;
24 import org.opendaylight.ovsdb.lib.table.Interface;
25 import org.opendaylight.ovsdb.lib.table.Port;
26 import org.opendaylight.ovsdb.lib.table.internal.Table;
27 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
28 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * OpenStack Neutron with the OpenVswitch data plan relies on a typical OVS bridge configurations that
34  * consists of br-int (Integration Bridge), br-tun (Tunnel bridge), br-ex (External bridge).
35  *
36  * In DevStack like setups, the br-tun is not automatically created on the controller nodes.
37  * Hence this class attempts to bring all the nodes to be elibible for OpenStack operations.
38  *
39  */
40 public class InternalNetworkManager {
41     static final Logger logger = LoggerFactory.getLogger(InternalNetworkManager.class);
42
43     private static InternalNetworkManager internalNetwork = new InternalNetworkManager();
44     private InternalNetworkManager() {
45     }
46
47     public static InternalNetworkManager getManager() {
48         return internalNetwork;
49     }
50
51     public String getInternalBridgeUUID (Node node, String bridgeName) {
52         try {
53             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
54             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
55             if (bridgeTable == null) return null;
56             for (String key : bridgeTable.keySet()) {
57                 Bridge bridge = (Bridge)bridgeTable.get(key);
58                 if (bridge.getName().equals(bridgeName)) return key;
59             }
60         } catch (Exception e) {
61             logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
62         }
63         return null;
64     }
65
66     public boolean isInternalNetworkNeutronReady(Node node) throws Exception {
67         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getIntegrationBridgeName()) != null) {
68             return true;
69         } else {
70             return false;
71         }
72     }
73
74     public boolean isInternalNetworkOverlayReady(Node node) throws Exception {
75         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName()) != null) {
76             return true;
77         } else {
78             return false;
79         }
80     }
81
82     public Status createInternalNetworkForOverlay(Node node) throws Exception {
83         if (!isInternalNetworkNeutronReady(node)) {
84             logger.error("Integration Bridge is not available in Node {}", node);
85             return new Status(StatusCode.NOTACCEPTABLE, "Integration Bridge is not avaialble in Node " + node);
86         }
87         if (isInternalNetworkOverlayReady(node)) {
88             logger.error("Network Overlay Bridge is already present in Node {}", node);
89             return new Status(StatusCode.NOTACCEPTABLE, "Network Overlay Bridge is already present in Node " + node);
90         }
91
92         /*
93          * Lets create this :
94          *
95          * Bridge br-tun
96                 Port patch-int
97                     Interface patch-int
98                         type: patch
99                         options: {peer=patch-tun}
100                 Port br-tun
101                     Interface br-tun
102                         type: internal
103          */
104
105         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
106         Bridge brTun = new Bridge();
107         brTun.setName(AdminConfigManager.getManager().getTunnelBridgeName());
108         // Create br-tun bridge
109         Status status = ovsdbTable.insertRow(node, Bridge.NAME.getName(), null, brTun);
110         if (!status.isSuccess()) return status;
111         String bridgeUUID = status.getDescription();
112         // Set OF Controller
113         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
114         connectionService.setOFController(node, bridgeUUID);
115
116         Port port = new Port();
117         port.setName(brTun.getName());
118         status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
119
120         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
121         String patchTun = AdminConfigManager.getManager().getPatchToTunnel();
122
123         status = addPatchPort(node, bridgeUUID, patchInt, patchTun);
124         if (!status.isSuccess()) return status;
125
126         // Create the corresponding patch-tun port in br-int
127         Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName());
128         for (String brIntUUID : bridges.keySet()) {
129             Bridge brInt = (Bridge) bridges.get(brIntUUID);
130             if (brInt.getName().equalsIgnoreCase(AdminConfigManager.getManager().getIntegrationBridgeName())) {
131                 return addPatchPort(node, brIntUUID, patchTun, patchInt);
132             }
133         }
134
135         return status;
136     }
137
138     private Status addPatchPort (Node node, String bridgeUUID, String portName, String patchName) throws Exception {
139         Status status = null;
140         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
141
142         Port patchPort = new Port();
143         patchPort.setName(portName);
144         // Create patch-int port and interface
145         status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, patchPort);
146         if (!status.isSuccess()) return status;
147
148         String patchPortUUID = status.getDescription();
149
150         String interfaceUUID = null;
151         int timeout = 6;
152         while ((interfaceUUID == null) && (timeout > 0)) {
153             patchPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), patchPortUUID);
154             OvsDBSet<UUID> interfaces = patchPort.getInterfaces();
155             if (interfaces == null || interfaces.size() == 0) {
156                 // Wait for the OVSDB update to sync up the Local cache.
157                 Thread.sleep(500);
158                 timeout--;
159                 continue;
160             }
161             interfaceUUID = interfaces.toArray()[0].toString();
162         }
163
164         if (interfaceUUID == null) {
165             return new Status(StatusCode.INTERNALERROR);
166         }
167
168         Interface tunInterface = new Interface();
169         tunInterface.setType("patch");
170         OvsDBMap<String, String> options = new OvsDBMap<String, String>();
171         options.put("peer", patchName);
172         tunInterface.setOptions(options);
173         status = ovsdbTable.updateRow(node, Interface.NAME.getName(), patchPortUUID, interfaceUUID, tunInterface);
174
175         return status;
176     }
177
178     private void prepareInternalNetwork (NeutronNetwork network, Node node) {
179         // vlan, vxlan, and gre
180         if (network.getProviderNetworkType().equalsIgnoreCase("gre") ||
181                 network.getProviderNetworkType().equalsIgnoreCase("vxlan") ||
182                 network.getProviderNetworkType().equalsIgnoreCase("vlan")) {
183
184             try {
185                 if (!this.isInternalNetworkOverlayReady(node)) {
186                     this.createInternalNetworkForOverlay(node);
187                 }
188             } catch (Exception e) {
189                 e.printStackTrace();
190             }
191         } else {
192             try {
193                 if (!this.isInternalNetworkNeutronReady(node)) {
194                     // TODO : FILL IN
195                     // this.createInternalNetworkForNeutron(node);
196                 }
197             } catch (Exception e) {
198                 e.printStackTrace();
199             }
200         }
201
202         this.initializeOFNormalFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
203         /*
204          * Disabling LLDP Punt rules to Controller for now.
205          * In a multi-node setup, it seems to cause issues with the Controller (CPU spike and some OF message timeouts).
206          * It is better to debug that and once we have an idea, we can enable this.
207          * There is NO functional impact to the Neutron integration with OVSDB by disabling LLDP flow installation.
208          */
209         /*
210         this.initializeLLDPFlowRules(node, AdminConfigManager.getManager().getTunnelBridgeName());
211         this.initializeLLDPFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
212         */
213     }
214
215     private void initializeOFNormalFlowRules(Node node, String bridgeName) {
216         String brIntId = this.getInternalBridgeUUID(node, bridgeName);
217         if (brIntId == null) {
218             logger.error("Failed to initialize Flow Rules for {}", node);
219             return;
220         }
221         try {
222             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
223             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
224             Set<String> dpids = bridge.getDatapath_id();
225             if (dpids == null || dpids.size() ==  0) return;
226             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
227             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
228             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
229                     IForwardingRulesManager.class, "default", this);
230             String flowName = ActionType.HW_PATH.toString();
231             if (frm.getStaticFlow(flowName, ofNode) != null) {
232                 logger.debug("Static Flow {} already programmed in the node {}", flowName, ofNode);
233                 return;
234             }
235             FlowConfig flow = new FlowConfig();
236             flow.setName("IntegrationBridgeNormal");
237             flow.setNode(ofNode);
238             flow.setPriority("1");
239             List<String> normalAction = new ArrayList<String>();
240             normalAction.add(flowName);
241             flow.setActions(normalAction);
242             Status status = frm.addStaticFlow(flow);
243             logger.debug("Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
244         } catch (Exception e) {
245             logger.error("Failed to initialize Flow Rules for {}", node, e);
246         }
247     }
248
249     private void initializeLLDPFlowRules(Node node, String bridgeName) {
250         String brIntId = this.getInternalBridgeUUID(node, bridgeName);
251         if (brIntId == null) {
252             logger.error("Failed to initialize Flow Rules for {}", node);
253             return;
254         }
255         try {
256             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
257             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
258             Set<String> dpids = bridge.getDatapath_id();
259             if (dpids == null || dpids.size() ==  0) return;
260             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
261             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
262             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
263                     IForwardingRulesManager.class, "default", this);
264             String flowName = "PuntLLDP";
265             if (frm.getStaticFlow(flowName, ofNode) != null) {
266                 logger.debug("Static Flow {} already programmed in the node {}", flowName, ofNode);
267                 return;
268             }
269
270             List<String> puntAction = new ArrayList<String>();
271             puntAction.add(ActionType.CONTROLLER.toString());
272
273             FlowConfig allowLLDP = new FlowConfig();
274             allowLLDP.setInstallInHw(true);
275             allowLLDP.setName(flowName);
276             allowLLDP.setPriority("10");
277             allowLLDP.setNode(ofNode);
278             allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue())
279                     .toUpperCase());
280             allowLLDP.setActions(puntAction);
281             Status status = frm.addStaticFlow(allowLLDP);
282             logger.debug("Flow Programming Status {} for Flow {} on {} / {}", status, allowLLDP, ofNode, node);
283         } catch (Exception e) {
284             logger.error("Failed to initialize Flow Rules for {}", node, e);
285         }
286     }
287
288     public void prepareInternalNetwork(NeutronNetwork network) {
289         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
290         List<Node> nodes = connectionService.getNodes();
291         for (Node node : nodes) {
292             prepareInternalNetwork(network, node);
293         }
294     }
295
296     public void prepareInternalNetwork(Node node) {
297         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
298         List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks();
299         for (NeutronNetwork network : networks) {
300             prepareInternalNetwork(network, node);
301         }
302     }
303
304     public static List safe( List other ) {
305         return other == null ? Collections.EMPTY_LIST : other;
306     }
307 }