Merge "Add UT for SouthboundMapper and SouthboundProvider"
[netvirt.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / impl / BridgeConfigurationManagerImpl.java
1 /*
2  * Copyright (c) 2013, 2015 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.ovsdb.openstack.netvirt.impl;
10
11 import org.opendaylight.neutron.spi.NeutronNetwork;
12 import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
13 import org.opendaylight.ovsdb.openstack.netvirt.NetworkHandler;
14 import org.opendaylight.ovsdb.openstack.netvirt.api.BridgeConfigurationManager;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
16 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
17 import org.opendaylight.ovsdb.openstack.netvirt.api.NetworkingProviderManager;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.OvsdbTables;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
20 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
21 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagerEntry;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
26
27 import com.google.common.base.Preconditions;
28 import com.google.common.collect.Lists;
29
30 import java.net.InetAddress;
31 import java.net.NetworkInterface;
32 import java.net.UnknownHostException;
33 import java.util.ArrayList;
34 import java.util.Enumeration;
35 import java.util.List;
36
37 import org.apache.commons.lang3.tuple.ImmutablePair;
38 import org.osgi.framework.ServiceReference;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * @author Madhu Venugopal
44  * @author Brent Salisbury
45  * @author Sam Hague (shague@redhat.com)
46  */
47 public class BridgeConfigurationManagerImpl implements BridgeConfigurationManager, ConfigInterface {
48     private static final Logger LOG = LoggerFactory.getLogger(BridgeConfigurationManagerImpl.class);
49
50     // The implementation for each of these services is resolved by the OSGi Service Manager
51     private volatile ConfigurationService configurationService;
52     private volatile NetworkingProviderManager networkingProviderManager;
53     private volatile Southbound southbound;
54
55     public void setConfigurationService(ConfigurationService configurationService) {
56         this.configurationService = configurationService;
57     }
58
59     public void setSouthbound(Southbound southbound) {
60         this.southbound = southbound;
61     }
62
63     @Override
64     public String getBridgeUuid(Node node, String bridgeName) {
65         return southbound.getBridgeUuid(node, bridgeName);
66     }
67
68     @Override
69     public boolean isNodeNeutronReady(Node node) {
70         Preconditions.checkNotNull(configurationService);
71         return southbound.getBridge(node, configurationService.getIntegrationBridgeName()) != null;
72     }
73
74     @Override
75     public boolean isNodeOverlayReady(Node node) {
76         Preconditions.checkNotNull(configurationService);
77         return isNodeNeutronReady(node)
78                 && southbound.getBridge(node, configurationService.getNetworkBridgeName()) != null;
79     }
80
81     @Override
82     public boolean isPortOnBridge (Node bridgeNode, String portName) {
83         return southbound.extractTerminationPointAugmentation(bridgeNode, portName) != null;
84     }
85
86     @Override
87     public boolean isNodeTunnelReady(Node bridgeNode, Node ovsdbNode) {
88         Preconditions.checkNotNull(configurationService);
89         if (!southbound.isBridgeOnOvsdbNode(ovsdbNode, configurationService.getIntegrationBridgeName())) {
90             LOG.trace("isNodeTunnelReady: node: {}, {} missing",
91                     bridgeNode, configurationService.getIntegrationBridgeName());
92             return false;
93         }
94
95         return isNodeL3Ready(bridgeNode, ovsdbNode);
96     }
97
98     @Override
99     public boolean isNodeVlanReady(Node bridgeNode, Node ovsdbNode, NeutronNetwork network) {
100         Preconditions.checkNotNull(configurationService);
101
102         final String brInt = configurationService.getIntegrationBridgeName();
103         if (!southbound.isBridgeOnOvsdbNode(ovsdbNode, brInt)) {
104             LOG.trace("isNodeVlanReady: node: {}, {} missing", bridgeNode, brInt);
105             return false;
106         }
107
108         /* Check if physical device is added to br-int. */
109         String phyNetName = getPhysicalInterfaceName(ovsdbNode, network.getProviderPhysicalNetwork());
110         if (!isPortOnBridge(bridgeNode, phyNetName)) {
111             LOG.trace("isNodeVlanReady: node: {}, eth missing", bridgeNode);
112             return false;
113         }
114
115         return isNodeL3Ready(bridgeNode, ovsdbNode);
116     }
117
118     public boolean isNodeL3Ready(Node bridgeNode, Node ovsdbNode) {
119         Preconditions.checkNotNull(configurationService);
120         boolean ready = false;
121         if (configurationService.isL3ForwardingEnabled()) {
122             final String brInt = configurationService.getIntegrationBridgeName();
123             final String brExt = configurationService.getExternalBridgeName();
124             final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
125             final String portNameExt = configurationService.getPatchPortName(new ImmutablePair<>(brExt, brInt));
126             Preconditions.checkNotNull(portNameInt);
127             Preconditions.checkNotNull(portNameExt);
128
129             if (southbound.isBridgeOnOvsdbNode(ovsdbNode, brExt)) {
130                 ready = isNetworkPatchCreated(bridgeNode, southbound.readBridgeNode(ovsdbNode, brExt));
131             } else {
132                 LOG.trace("isNodeL3Ready: node: {}, {} missing",
133                         bridgeNode, brExt);
134             }
135         } else {
136             ready = true;
137         }
138         return ready;
139     }
140
141     @Override
142     public void prepareNode(Node ovsdbNode) {
143         Preconditions.checkNotNull(configurationService);
144
145         try {
146             createIntegrationBridge(ovsdbNode);
147         } catch (Exception e) {
148             LOG.error("Error creating Integration Bridge on {}", ovsdbNode, e);
149             return;
150         }
151
152         try {
153             if (configurationService.isL3ForwardingEnabled()) {
154                 createExternalBridge(ovsdbNode);
155             }
156         } catch (Exception e) {
157             LOG.error("Error creating External Bridge on {}", ovsdbNode, e);
158             return;
159         }
160         // this node is an ovsdb node so it doesn't have a bridge
161         // so either look up the bridges or just wait for the bridge update to come in
162         // and add the flows there.
163         //networkingProviderManager.getProvider(node).initializeFlowRules(node);
164     }
165
166     /**
167      * Check if the full network setup is available. If not, create it.
168      */
169     @Override
170     public boolean createLocalNetwork (Node bridgeNode, NeutronNetwork network) {
171         boolean isCreated = false;
172         Node ovsdbNode = southbound.readOvsdbNode(bridgeNode);
173         if (ovsdbNode == null) {
174             //this should never happen
175             LOG.error("createLocalNetwork could not find ovsdbNode from bridge node " + bridgeNode);
176             return false;
177         }
178         if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
179             if (!isNodeVlanReady(bridgeNode, ovsdbNode, network)) {
180                 try {
181                     isCreated = createBridges(bridgeNode, ovsdbNode, network);
182                 } catch (Exception e) {
183                     LOG.error("Error creating internal vlan net network " + bridgeNode, e);
184                 }
185             } else {
186                 isCreated = true;
187             }
188         } else if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
189                    network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
190             if (!isNodeTunnelReady(bridgeNode, ovsdbNode)) {
191                 try {
192                     isCreated = createBridges(bridgeNode, ovsdbNode, network);
193                 } catch (Exception e) {
194                     LOG.error("Error creating internal vxlan/gre net network " + bridgeNode, e);
195                 }
196             } else {
197                 isCreated = true;
198             }
199         }
200         return isCreated;
201     }
202
203     @Override
204     public String getPhysicalInterfaceName (Node node, String physicalNetwork) {
205         String phyIf = null;
206         String providerMaps = southbound.getOtherConfig(node, OvsdbTables.OPENVSWITCH,
207                 configurationService.getProviderMappingsKey());
208         if (providerMaps == null) {
209             providerMaps = configurationService.getDefaultProviderMapping();
210         }
211
212         if (providerMaps != null) {
213             for (String map : providerMaps.split(",")) {
214                 String[] pair = map.split(":");
215                 if (pair[0].equals(physicalNetwork)) {
216                     phyIf = pair[1];
217                     break;
218                 }
219             }
220         }
221
222         if (phyIf == null) {
223             LOG.error("Physical interface not found for Node: {}, Network {}",
224                     node, physicalNetwork);
225         }
226
227         return phyIf;
228     }
229
230     @Override
231     public List<String> getAllPhysicalInterfaceNames(Node node) {
232         List<String> phyIfName = Lists.newArrayList();
233         String providerMaps = southbound.getOtherConfig(node, OvsdbTables.OPENVSWITCH,
234                 configurationService.getProviderMappingsKey());
235         if (providerMaps == null) {
236             providerMaps = configurationService.getDefaultProviderMapping();
237         }
238
239         if (providerMaps != null) {
240             for (String map : providerMaps.split(",")) {
241                 String[] pair = map.split(":");
242                 phyIfName.add(pair[1]);
243             }
244         }
245
246         return phyIfName;
247     }
248
249     /**
250      * Returns true if a patch port exists between the Integration Bridge and Network Bridge
251      */
252     private boolean isNetworkPatchCreated(Node intBridge, Node netBridge) {
253         Preconditions.checkNotNull(configurationService);
254
255         boolean isPatchCreated = false;
256
257         String portName = configurationService.getPatchPortName(new ImmutablePair<>(intBridge, netBridge));
258         if (isPortOnBridge(intBridge, portName)) {
259             portName = configurationService.getPatchPortName(new ImmutablePair<>(netBridge, intBridge));
260             if (isPortOnBridge(netBridge, portName)) {
261                 isPatchCreated = true;
262             }
263         }
264
265         return isPatchCreated;
266     }
267
268     /**
269      * Creates the Integration Bridge
270      */
271     private boolean createIntegrationBridge(Node ovsdbNode) {
272         Preconditions.checkNotNull(configurationService);
273
274         if (!addBridge(ovsdbNode, configurationService.getIntegrationBridgeName())) {
275             LOG.debug("Integration Bridge Creation failed");
276             return false;
277         }
278         return true;
279     }
280
281     private boolean createExternalBridge(Node ovsdbNode) {
282         Preconditions.checkNotNull(configurationService);
283
284         if (!addBridge(ovsdbNode, configurationService.getExternalBridgeName())) {
285             LOG.debug("External Bridge Creation failed");
286             return false;
287         }
288         return true;
289     }
290
291     /**
292      * Create and configure bridges for all network types and OpenFlow versions.
293      *
294        OF 1.0 vlan:
295        Bridge br-int
296             Port patch-net
297                 Interface patch-net
298                     type: patch
299                     options: {peer=patch-int}
300             Port br-int
301                 Interface br-int
302                     type: internal
303        Bridge br-net
304             Port "eth1"
305                 Interface "eth1"
306             Port patch-int
307                 Interface patch-int
308                     type: patch
309                     options: {peer=patch-net}
310             Port br-net
311                 Interface br-net
312                     type: internal
313
314        OF 1.0 tunnel:
315        Bridge br-int
316             Port patch-net
317                 Interface patch-net
318                     type: patch
319                     options: {peer=patch-int}
320             Port br-int
321                 Interface br-int
322                     type: internal
323        Bridge "br-net"
324             Port patch-int
325                 Interface patch-int
326                     type: patch
327                     options: {peer=patch-net}
328             Port br-net
329                 Interface br-net
330                     type: internal
331
332        OF 1.3 vlan:
333        Bridge br-int
334             Port "eth1"
335                 Interface "eth1"
336             Port br-int
337                 Interface br-int
338                     type: internal
339
340        OF 1.3 tunnel:
341        Bridge br-int
342             Port br-int
343                 Interface br-int
344                     type: internal
345      */
346     private boolean createBridges(Node bridgeNode, Node ovsdbNode, NeutronNetwork network) {
347         Preconditions.checkNotNull(configurationService);
348         Preconditions.checkNotNull(networkingProviderManager);
349
350         LOG.debug("createBridges: node: {}, network type: {}", bridgeNode, network.getProviderNetworkType());
351
352         final String brInt = configurationService.getIntegrationBridgeName();
353         if (! createIntegrationBridge(ovsdbNode)) {
354             LOG.debug("{} Bridge creation failed", brInt);
355             return false;
356         }
357
358         if (configurationService.isL3ForwardingEnabled()) {
359             final String brExt = configurationService.getExternalBridgeName();
360             if (! createExternalBridge(ovsdbNode)) {
361                 LOG.error("{} Bridge creation failed", brExt);
362                 return false;
363             }
364
365             //get two patch port names
366             final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
367             final String portNameExt = configurationService.getPatchPortName(new ImmutablePair<>(brExt, brInt));
368             Preconditions.checkNotNull(portNameInt);
369             Preconditions.checkNotNull(portNameExt);
370
371             if (!addPatchPort(bridgeNode, brInt, portNameInt, portNameExt)) {
372                 LOG.error("Add Port {} to Bridge {} failed", portNameInt, brInt);
373                 return false;
374             }
375             Node extBridgeNode = southbound.readBridgeNode(ovsdbNode, brExt);
376             Preconditions.checkNotNull(extBridgeNode);
377             if (!addPatchPort(extBridgeNode, brExt, portNameExt, portNameInt)) {
378                 LOG.error("Add Port {} to Bridge {} failed", portNameExt, brExt);
379                 return false;
380             }
381         }
382
383         /* For vlan network types add physical port to br-int. */
384         if (network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
385             String phyNetName = this.getPhysicalInterfaceName(bridgeNode, network.getProviderPhysicalNetwork());
386             if (!addPortToBridge(bridgeNode, brInt, phyNetName)) {
387                 LOG.debug("Add Port {} to Bridge {} failed", phyNetName, brInt);
388                 return false;
389             }
390         }
391
392         LOG.debug("createBridges: node: {}, status: success", bridgeNode);
393         return true;
394     }
395
396     /**
397      * Add a Port to a Bridge
398      */
399     private boolean addPortToBridge (Node node, String bridgeName, String portName) {
400         boolean rv = true;
401
402         if (southbound.extractTerminationPointAugmentation(node, portName) == null) {
403             rv = southbound.addTerminationPoint(node, bridgeName, portName, null);
404
405             if (rv) {
406                 LOG.info("addPortToBridge: node: {}, bridge: {}, portname: {} status: success",
407                         node.getNodeId().getValue(), bridgeName, portName);
408             } else {
409                 LOG.error("addPortToBridge: node: {}, bridge: {}, portname: {} status: FAILED",
410                         node.getNodeId().getValue(), bridgeName, portName);
411             }
412         } else {
413             LOG.trace("addPortToBridge: node: {}, bridge: {}, portname: {} status: not_needed",
414                     node.getNodeId().getValue(), bridgeName, portName);
415         }
416
417         return rv;
418     }
419
420     /**
421      * Add a Patch Port to a Bridge
422      */
423     private boolean addPatchPort (Node node, String bridgeName, String portName, String peerPortName) {
424         boolean rv = true;
425
426         if (southbound.extractTerminationPointAugmentation(node, portName) == null) {
427             rv = southbound.addPatchTerminationPoint(node, bridgeName, portName, peerPortName);
428
429             if (rv) {
430                 LOG.info("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: success",
431                         node.getNodeId().getValue(), bridgeName, portName, peerPortName);
432             } else {
433                 LOG.error("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: FAILED",
434                         node.getNodeId().getValue(), bridgeName, portName, peerPortName);
435             }
436         } else {
437             LOG.trace("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: not_needed",
438                     node.getNodeId().getValue(), bridgeName, portName, peerPortName);
439         }
440
441         return rv;
442     }
443
444     /**
445      * Add Bridge to a Node
446      */
447     private boolean addBridge(Node ovsdbNode, String bridgeName) {
448         boolean rv = true;
449         if ((!southbound.isBridgeOnOvsdbNode(ovsdbNode, bridgeName)) ||
450                 (southbound.getBridgeFromConfig(ovsdbNode, bridgeName) == null)) {
451             rv = southbound.addBridge(ovsdbNode, bridgeName, getControllersFromOvsdbNode(ovsdbNode));
452         }
453         return rv;
454     }
455
456     private String getControllerIPAddress() {
457         String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
458         if (addressString != null) {
459             try {
460                 if (InetAddress.getByName(addressString) != null) {
461                     return addressString;
462                 }
463             } catch (UnknownHostException e) {
464                 LOG.error("Host {} is invalid", addressString);
465             }
466         }
467
468         addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
469         if (addressString != null) {
470             try {
471                 if (InetAddress.getByName(addressString) != null) {
472                     return addressString;
473                 }
474             } catch (UnknownHostException e) {
475                 LOG.error("Host {} is invalid", addressString);
476             }
477         }
478
479         return null;
480     }
481
482     private short getControllerOFPort() {
483         short openFlowPort = Constants.OPENFLOW_PORT;
484         String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
485         if (portString != null) {
486             try {
487                 openFlowPort = Short.parseShort(portString);
488             } catch (NumberFormatException e) {
489                 LOG.warn("Invalid port:{}, use default({})", portString,
490                         openFlowPort);
491             }
492         }
493         return openFlowPort;
494     }
495
496     private List<String> getControllersFromOvsdbNode(Node node) {
497         List<String> controllersStr = new ArrayList<>();
498
499         String controllerIpStr = getControllerIPAddress();
500         if (controllerIpStr != null) {
501             // If codepath makes it here, the ip address to be used was explicitly provided.
502             // Being so, also fetch openflowPort provided via ConfigProperties.
503             controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
504                     + ":" + controllerIpStr + ":" + getControllerOFPort());
505         } else {
506             // Check if ovsdb node has manager entries
507             OvsdbNodeAugmentation ovsdbNodeAugmentation = southbound.extractOvsdbNode(node);
508             if (ovsdbNodeAugmentation != null) {
509                 List<ManagerEntry> managerEntries = ovsdbNodeAugmentation.getManagerEntry();
510                 if (managerEntries != null && !managerEntries.isEmpty()) {
511                     for (ManagerEntry managerEntry : managerEntries) {
512                         if (managerEntry == null || managerEntry.getTarget() == null) {
513                             continue;
514                         }
515                         String[] tokens = managerEntry.getTarget().getValue().split(":");
516                         if (tokens.length == 3 && tokens[0].equalsIgnoreCase("tcp")) {
517                             controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
518                                     + ":" + tokens[1] + ":" + getControllerOFPort());
519                         } else {
520                             LOG.trace("Skipping manager entry {} for node {}",
521                                     managerEntry.getTarget(), node.getNodeId().getValue());
522                         }
523                     }
524                 } else {
525                     LOG.warn("Ovsdb Node does not contain manager entries : {}", node);
526                 }
527             }
528         }
529
530         if (controllersStr.isEmpty()) {
531             // Neither user provided ip nor ovsdb node has manager entries. Lets use local machine ip address.
532             LOG.debug("Use local machine ip address as a OpenFlow Controller ip address");
533             controllerIpStr = getLocalControllerHostIpAddress();
534             if (controllerIpStr != null) {
535                 controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
536                         + ":" + controllerIpStr + ":" + Constants.OPENFLOW_PORT);
537             }
538         }
539
540         if (controllersStr.isEmpty()) {
541             LOG.warn("Failed to determine OpenFlow controller ip address");
542         } else if (LOG.isDebugEnabled()) {
543             controllerIpStr = "";
544             for (String currControllerIpStr : controllersStr) {
545                 controllerIpStr += " " + currControllerIpStr;
546             }
547             LOG.debug("Found {} OpenFlow Controller(s) :{}", controllersStr.size(), controllerIpStr);
548         }
549
550         return controllersStr;
551     }
552
553     private String getLocalControllerHostIpAddress() {
554         String ipaddress = null;
555         try{
556             for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();ifaces.hasMoreElements();){
557                 NetworkInterface iface = ifaces.nextElement();
558
559                 for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
560                     InetAddress inetAddr = inetAddrs.nextElement();
561                     if (!inetAddr.isLoopbackAddress() && inetAddr.isSiteLocalAddress()) {
562                         ipaddress = inetAddr.getHostAddress();
563                         break;
564                     }
565                 }
566             }
567         }catch (Exception e){
568             LOG.warn("Exception while fetching local host ip address ", e);
569         }
570         return ipaddress;
571     }
572
573     @Override
574     public void setDependencies(ServiceReference serviceReference) {
575         configurationService =
576                 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
577         networkingProviderManager =
578                 (NetworkingProviderManager) ServiceHelper.getGlobalInstance(NetworkingProviderManager.class, this);
579         southbound =
580                 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
581     }
582
583     @Override
584     public void setDependencies(Object impl) {
585     }
586 }