Fix bug 1028: Don't ignore exception in prepareInternalNetwork
[netvirt.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / InternalNetworkManager.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, Sam Hague
9  */
10 package org.opendaylight.ovsdb.neutron;
11
12 import java.util.Map;
13
14 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
15 import org.opendaylight.controller.sal.core.Node;
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.provider.ProviderNetworkManager;
27 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
28 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
29 import org.opendaylight.ovsdb.plugin.StatusWithUuid;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * OpenStack Neutron with the OpenvSwitch data plan relies on a typical OVS bridge configurations that
35  * consists of br-int (Integration Bridge), br-tun (Tunnel bridge), br-ex (External bridge).
36  *
37  * In DevStack like setups, the br-tun is not automatically created on the controller nodes.
38  * Hence this class attempts to bring all the nodes to be eligible for OpenStack operations.
39  *
40  */
41 public class InternalNetworkManager {
42     static final Logger logger = LoggerFactory.getLogger(InternalNetworkManager.class);
43     private static final int LLDP_PRIORITY = 1000;
44     private static final int NORMAL_PRIORITY = 0;
45
46     private static InternalNetworkManager internalNetwork = new InternalNetworkManager();
47     private InternalNetworkManager() {
48     }
49
50     public static InternalNetworkManager getManager() {
51         return internalNetwork;
52     }
53
54     public String getInternalBridgeUUID (Node node, String bridgeName) {
55         try {
56             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
57             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
58             if (bridgeTable == null) return null;
59             for (String key : bridgeTable.keySet()) {
60                 Bridge bridge = (Bridge)bridgeTable.get(key);
61                 if (bridge.getName().equals(bridgeName)) return key;
62             }
63         } catch (Exception e) {
64             logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
65         }
66         return null;
67     }
68
69     public Bridge getInternalBridge (Node node, String bridgeName) {
70         try {
71             OVSDBConfigService ovsdbTable = (OVSDBConfigService) ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
72             Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
73             if (bridgeTable != null) {
74                 for (String key : bridgeTable.keySet()) {
75                     Bridge bridge = (Bridge) bridgeTable.get(key);
76                     if (bridge.getName().equals(bridgeName)) {
77                         return bridge;
78                     }
79                 }
80             }
81         } catch (Exception e) {
82             logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
83         }
84         return null;
85     }
86
87     public boolean isInternalNetworkNeutronReady(Node node) {
88         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getIntegrationBridgeName()) != null) {
89             return true;
90         } else {
91             return false;
92         }
93     }
94
95     public boolean isInternalNetworkOverlayReady(Node node) {
96         if (!this.isInternalNetworkNeutronReady(node)) {
97             return false;
98         }
99         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getNetworkBridgeName()) != null) {
100             return true;
101         } else {
102             return false;
103         }
104     }
105
106     public boolean isPortOnBridge (Node node, Bridge bridge, String portName) {
107         OVSDBConfigService ovsdbTable = (OVSDBConfigService) ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
108
109         for (UUID portsUUID : bridge.getPorts()) {
110             try {
111                 Port port = (Port) ovsdbTable.getRow(node, Port.NAME.getName(), portsUUID.toString());
112                 if ((port != null) && port.getName().equalsIgnoreCase(portName)) {
113                     return true;
114                 }
115             } catch (Exception e) {
116                 logger.error("Error getting port {} for bridge domain {}/{}", portsUUID, node, bridge.getName(), e);
117             }
118         }
119
120         return false;
121     }
122
123     public boolean isNetworkPatchCreated (Node node, Bridge intBridge, Bridge netBridge) {
124         boolean isPatchCreated = false;
125
126         String portName = AdminConfigManager.getManager().getPatchToNetwork();
127         if (isPortOnBridge(node, intBridge, portName)) {
128             portName = AdminConfigManager.getManager().getPatchToIntegration();
129             if (isPortOnBridge(node, netBridge, portName)) {
130                 isPatchCreated = true;
131             }
132         }
133
134         return isPatchCreated;
135     }
136
137     /* Determine if internal network is ready for tunnel network types.
138      * - OF 1.0 requires br-int, br-net and a patch connecting them.
139      * - OF 1.3 requires br-int.
140      */
141     public boolean isInternalNetworkTunnelReady (Node node) {
142         /* Is br-int created? */
143         Bridge intBridge = this.getInternalBridge(node, AdminConfigManager.getManager().getIntegrationBridgeName());
144         if (intBridge == null) {
145             return false;
146         }
147
148         if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
149             /* Is br-net created? */
150             Bridge netBridge = this.getInternalBridge(node, AdminConfigManager.getManager().getNetworkBridgeName());
151             if (netBridge == null) {
152                 return false;
153             }
154
155             if (!isNetworkPatchCreated(node, intBridge, netBridge)) {
156                 return false;
157             }
158         }
159         return true;
160     }
161
162     /* Determine if internal network is ready for vlan network types.
163      * - OF 1.0 requires br-int, br-net, a patch connecting them and
164      * physical device added to br-net.
165      * - OF 1.3 requires br-int and physical device added to br-int.
166      */
167     public boolean isInternalNetworkVlanReady (Node node, NeutronNetwork network) {
168         /* is br-int created */
169         Bridge intBridge = this.getInternalBridge(node, AdminConfigManager.getManager().getIntegrationBridgeName());
170         if (intBridge == null) {
171             logger.trace("shague isInternalNetworkVlanReady: node: {}, br-int missing", node);
172             return false;
173         }
174
175         if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
176             /* is br-net created? */
177             Bridge netBridge = this.getInternalBridge(node, AdminConfigManager.getManager().getNetworkBridgeName());
178
179             if (netBridge == null) {
180                 logger.trace("shague isInternalNetworkVlanReady: node: {}, br-net missing", node);
181                 return false;
182             }
183
184             if (!isNetworkPatchCreated(node, intBridge, netBridge)) {
185                 logger.trace("shague isInternalNetworkVlanReady: node: {}, patch missing", node);
186                 return false;
187             }
188
189             /* Check if physical device is added to br-net. */
190             String phyNetName = AdminConfigManager.getManager().getPhysicalInterfaceName(node, network.getProviderPhysicalNetwork());
191             if (isPortOnBridge(node, netBridge, phyNetName)) {
192                 return true;
193             }
194         } else {
195             /* Check if physical device is added to br-int. */
196             String phyNetName = AdminConfigManager.getManager().getPhysicalInterfaceName(node, network.getProviderPhysicalNetwork());
197             if (isPortOnBridge(node, intBridge, phyNetName)) {
198                 return true;
199             }
200         }
201
202         logger.trace("shague isInternalNetworkVlanReady: node: {}, eth missing", node);
203         return false;
204     }
205
206     /*
207      * Create the integration bridge.
208      *
209        Bridge br-int
210             Port br-int
211                 Interface br-int
212                     type: internal
213      */
214     public void createIntegrationBridge (Node node) throws Exception {
215         String brInt = AdminConfigManager.getManager().getIntegrationBridgeName();
216
217         Status status = this.addInternalBridge(node, brInt, null, null);
218         if (!status.isSuccess()) {
219             logger.debug("Integration Bridge Creation Status: {}", status);
220         }
221     }
222
223     /*
224      * Create complete network for all network types and OpenFlow versions.
225      *
226        OF 1.0 vlan:
227        Bridge br-int
228             Port patch-net
229                 Interface patch-net
230                     type: patch
231                     options: {peer=patch-int}
232             Port br-int
233                 Interface br-int
234                     type: internal
235        Bridge br-net
236             Port "eth1"
237                 Interface "eth1"
238             Port patch-int
239                 Interface patch-int
240                     type: patch
241                     options: {peer=patch-net}
242             Port br-net
243                 Interface br-net
244                     type: internal
245
246        OF 1.0 tunnel:
247        Bridge br-int
248             Port patch-net
249                 Interface patch-net
250                     type: patch
251                     options: {peer=patch-int}
252             Port br-int
253                 Interface br-int
254                     type: internal
255        Bridge "br-net"
256             Port patch-int
257                 Interface patch-int
258                     type: patch
259                     options: {peer=patch-net}
260             Port br-net
261                 Interface br-net
262                     type: internal
263
264        OF 1.3 vlan:
265        Bridge br-int
266             Port "eth1"
267                 Interface "eth1"
268             Port br-int
269                 Interface br-int
270                     type: internal
271
272        OF 1.3 tunnel:
273        Bridge br-int
274             Port br-int
275                 Interface br-int
276                     type: internal
277      */
278     public boolean createNetNetwork (Node node, NeutronNetwork network) throws Exception {
279         Status status;
280
281         logger.debug("createNetNetwork: node: {}, network type: {}", node, network.getProviderNetworkType());
282
283         if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) { /* indicates OF 1.0 */
284             String brInt = AdminConfigManager.getManager().getIntegrationBridgeName();
285             String brNet = AdminConfigManager.getManager().getNetworkBridgeName();
286             String patchNet = AdminConfigManager.getManager().getPatchToNetwork();
287             String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
288
289             status = this.addInternalBridge(node, brInt, patchNet, patchInt);
290             if (!status.isSuccess()) {
291                 logger.debug("{} Bridge Creation Status: {}", brInt, status);
292                 return false;
293             }
294             status = this.addInternalBridge(node, brNet, patchInt, patchNet);
295             if (!status.isSuccess()) {
296                 logger.debug("{} Bridge Creation Status: {}", brNet, status);
297                 return false;
298             }
299
300             /* For vlan network types add physical port to br-net. */
301             if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
302                 String phyNetName = AdminConfigManager.getManager().getPhysicalInterfaceName(node, network.getProviderPhysicalNetwork());
303                 status = addPortToBridge(node, brNet, phyNetName);
304                 if (!status.isSuccess()) {
305                     logger.debug("Add Port {} to Bridge {} Status: {}", phyNetName, brNet, status);
306                     return false;
307                 }
308             }
309         } else {
310             String brInt = AdminConfigManager.getManager().getIntegrationBridgeName();
311             status = this.addInternalBridge(node, brInt, null, null);
312             if (!status.isSuccess()) {
313                 logger.debug("{} Bridge Creation Status: {}", brInt, status);
314                 return false;
315             }
316
317             /* For vlan network types add physical port to br-int. */
318             if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
319                 String phyNetName = AdminConfigManager.getManager().getPhysicalInterfaceName(node, network.getProviderPhysicalNetwork());
320                 status = addPortToBridge(node, brInt, phyNetName);
321                 if (!status.isSuccess()) {
322                     logger.debug("Add Port {} to Bridge {} Status: {}", phyNetName, brInt, status);
323                     return false;
324                 }
325             }
326         }
327
328         logger.debug("createNetNetwork: node: {}, status: success", node);
329         return true;
330     }
331
332     private Status addPortToBridge (Node node, String bridgeName, String portName) throws Exception {
333         logger.debug("addPortToBridge: Adding port: {} to Bridge {}, Node {}", portName, bridgeName, node);
334         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
335
336         String bridgeUUID = this.getInternalBridgeUUID(node, bridgeName);
337         if (bridgeUUID == null) {
338             logger.error("addPortToBridge: Could not find Bridge {} in Node {}", bridgeName, node);
339             return new Status(StatusCode.NOTFOUND, "Could not find "+bridgeName+" in "+node);
340         }
341
342         /* Check if the port already exists. */
343         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
344         if (bridge != null) {
345             if (isPortOnBridge(node, bridge, portName)) {
346                 logger.debug("addPortToBridge: Port {} already in Bridge {}, Node {}", portName, bridgeName, node);
347                 return new Status(StatusCode.SUCCESS);
348             }
349         } else {
350             logger.error("addPortToBridge: Could not find Port {} in Bridge {}, Node {}", portName, bridgeName, node);
351             return new Status(StatusCode.NOTFOUND, "Could not find "+portName+" in "+bridgeName);
352         }
353
354         Port port = new Port();
355         port.setName(portName);
356         StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
357         if (!statusWithUuid.isSuccess()) {
358             logger.error("addPortToBridge: Failed to add Port {} in Bridge {}, Node {}", portName, bridgeName, node);
359             return statusWithUuid;
360         }
361
362         String portUUID = statusWithUuid.getUuid().toString();
363         String interfaceUUID = null;
364         int timeout = 6;
365         while ((interfaceUUID == null) && (timeout > 0)) {
366             port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID);
367             OvsDBSet<UUID> interfaces = port.getInterfaces();
368             if (interfaces == null || interfaces.size() == 0) {
369                 // Wait for the OVSDB update to sync up the Local cache.
370                 Thread.sleep(500);
371                 timeout--;
372                 continue;
373             }
374             interfaceUUID = interfaces.toArray()[0].toString();
375             Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), interfaceUUID);
376             if (intf == null) {
377                 interfaceUUID = null;
378             }
379         }
380
381         if (interfaceUUID == null) {
382             logger.error("addPortToBridge: Cannot identify Interface for port {}/{}", portName, portUUID);
383             return new Status(StatusCode.INTERNALERROR);
384         }
385
386         return new Status(StatusCode.SUCCESS);
387     }
388
389     private Status addPatchPort (Node node, String bridgeUUID, String portName, String peerPortName) throws Exception {
390         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
391
392         logger.debug("addPatchPort: node: {}, bridgeUUID: {}, port: {}, peer: {}",
393                 node, bridgeUUID, portName, peerPortName);
394
395         /* Check if the port already exists. */
396         Bridge bridge = (Bridge)ovsdbTable.getRow(node, Bridge.NAME.getName(), bridgeUUID);
397         if (bridge != null) {
398             if (isPortOnBridge(node, bridge, portName)) {
399                 logger.debug("addPatchPort: Port {} already in Bridge, Node {}", portName, node);
400                 return new Status(StatusCode.SUCCESS);
401             }
402         } else {
403             logger.error("addPatchPort: Could not find Port {} in Bridge, Node {}", portName, node);
404             return new Status(StatusCode.NOTFOUND, "Could not find "+portName+" in Bridge");
405         }
406
407         Port patchPort = new Port();
408         patchPort.setName(portName);
409         // Create patch port and interface
410         StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, patchPort);
411         if (!statusWithUuid.isSuccess()) return statusWithUuid;
412
413         String patchPortUUID = statusWithUuid.getUuid().toString();
414
415         String interfaceUUID = null;
416         int timeout = 6;
417         while ((interfaceUUID == null) && (timeout > 0)) {
418             patchPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), patchPortUUID);
419             OvsDBSet<UUID> interfaces = patchPort.getInterfaces();
420             if (interfaces == null || interfaces.size() == 0) {
421                 // Wait for the OVSDB update to sync up the Local cache.
422                 Thread.sleep(500);
423                 timeout--;
424                 continue;
425             }
426             interfaceUUID = interfaces.toArray()[0].toString();
427         }
428
429         if (interfaceUUID == null) {
430             return new Status(StatusCode.INTERNALERROR);
431         }
432
433         Interface intf = new Interface();
434         intf.setType("patch");
435         OvsDBMap<String, String> options = new OvsDBMap<String, String>();
436         options.put("peer", peerPortName);
437         intf.setOptions(options);
438         return ovsdbTable.updateRow(node, Interface.NAME.getName(), patchPortUUID, interfaceUUID, intf);
439     }
440
441     private Status addInternalBridge (Node node, String bridgeName, String localPatchName, String remotePatchName) throws Exception {
442         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
443
444         String bridgeUUID = this.getInternalBridgeUUID(node, bridgeName);
445         Bridge bridge = new Bridge();
446         OvsDBSet<String> failMode = new OvsDBSet<String>();
447         failMode.add("secure");
448         bridge.setFail_mode(failMode);
449
450         OvsDBSet<String> protocols = new OvsDBSet<String>();
451         if (!ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
452             protocols.add("OpenFlow13");
453         } else {
454             protocols.add("OpenFlow10");
455         }
456         bridge.setProtocols(protocols);
457
458         if (bridgeUUID == null) {
459             bridge.setName(bridgeName);
460
461             StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Bridge.NAME.getName(), null, bridge);
462             if (!statusWithUuid.isSuccess()) return statusWithUuid;
463             bridgeUUID = statusWithUuid.getUuid().toString();
464             Port port = new Port();
465             port.setName(bridgeName);
466             Status status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
467             logger.debug("addInternalBridge: Inserting Bridge {} {} with protocols {} and status {}",
468                     bridgeName, bridgeUUID, protocols, status);
469         } else {
470             Status status = ovsdbTable.updateRow(node, Bridge.NAME.getName(), null, bridgeUUID, bridge);
471             logger.debug("addInternalBridge: Updating Bridge {} {} with protocols {} and status {}",
472                     bridgeName, bridgeUUID, protocols, status);
473         }
474
475         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
476         connectionService.setOFController(node, bridgeUUID);
477
478         if (localPatchName != null && remotePatchName != null && ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
479             return addPatchPort(node, bridgeUUID, localPatchName, remotePatchName);
480         }
481         return new Status(StatusCode.SUCCESS);
482     }
483
484     public void prepareInternalNetwork(Node node) {
485         try {
486             this.createIntegrationBridge(node);
487         } catch (Exception e) {
488             logger.error("Error creating internal network "+node.toString(), e);
489             return;
490         }
491         ProviderNetworkManager.getManager().initializeFlowRules(node);
492     }
493
494     /*
495      * Check if the full network setup is available. If not, create it.
496      */
497     public boolean checkAndCreateNetwork (Node node, NeutronNetwork network) {
498         boolean isCreated = false;
499         if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
500             if (!InternalNetworkManager.getManager().isInternalNetworkVlanReady(node, network)) {
501                 try {
502                     isCreated = InternalNetworkManager.getManager().createNetNetwork(node, network);
503                 } catch (Exception e) {
504                     logger.error("Error creating internal net network ", node, e);
505                 }
506             } else {
507                 isCreated = true;
508             }
509         } else if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
510                 network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
511             if (!InternalNetworkManager.getManager().isInternalNetworkTunnelReady(node)) {
512                 try {
513                     isCreated = InternalNetworkManager.getManager().createNetNetwork(node, network);
514                 } catch (Exception e) {
515                     logger.error("Error creating internal net network ", node, e);
516                 }
517             } else {
518                 isCreated = true;
519             }
520         }
521
522         return isCreated;
523     }
524 }