Added br-tun bridge installation code via InternalNetworkManager in cases when the... 27/3027/2
authorMadhu Venugopal <mavenugo@gmail.com>
Sat, 23 Nov 2013 01:06:18 +0000 (17:06 -0800)
committerGerrit Code Review <gerrit@opendaylight.org>
Sun, 24 Nov 2013 19:22:27 +0000 (19:22 +0000)
In DevStack environement, the Controller node is also used as compute. But the OVS is not populated with the
appropriate internal networks (such as br-tun and the corresponding patch-ports).
Witht his commit, this condition is identified and appropriate installation is done on the br-tun and br-int.

Change-Id: I8f1f65da864bd8a016912b46dbed3ed9a926ef2e
Signed-off-by: Madhu Venugopal <mavenugo@gmail.com>
neutron/src/main/java/org/opendaylight/ovsdb/neutron/AdminConfigManager.java
neutron/src/main/java/org/opendaylight/ovsdb/neutron/InternalNetworkManager.java
neutron/src/main/java/org/opendaylight/ovsdb/neutron/NetworkHandler.java
neutron/src/main/java/org/opendaylight/ovsdb/neutron/SouthboundHandler.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/ConnectionService.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/IConnectionServiceInternal.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/InventoryService.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/InventoryServiceInternal.java

index 078824d888efd63028df100781fac3021f23dd27..a4d734385fa5d85c55d119ff8cd71a5883fbdaf7 100644 (file)
@@ -6,8 +6,10 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.ovsdb.lib.table.Open_vSwitch;
 import org.opendaylight.ovsdb.lib.table.internal.Table;
+import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -81,26 +83,49 @@ public class AdminConfigManager {
         tunnelEndpoints.put(node, address);
     }
 
-    public String getTunnelEndpointConfigTable() {
-        return "Open_vSwitch";
+    public boolean isInterested (String tableName) {
+        return tableName.equalsIgnoreCase("Open_vSwitch");
     }
 
+    private void populateTunnelEndpoint (Node node, Open_vSwitch row) {
+        Map<String, String> configs = row.getOther_config();
+        if (configs != null) {
+            String tunnelEndpoint = configs.get(tunnelEndpointConfigName);
+            if (tunnelEndpoint != null) {
+                try {
+                    InetAddress address = InetAddress.getByName(tunnelEndpoint);
+                    addTunnelEndpoint(node, address);
+                    logger.debug("Tunnel Endpoint for Node {} {}", node, address.getHostAddress());
+                } catch (UnknownHostException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public void populateTunnelEndpoint (Node node) {
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+        try {
+            Map<String, Table<?>> openvswitchTable = ovsdbTable.getRows(node, "Open_vSwitch");
+            if (openvswitchTable == null) {
+                logger.debug("Open_vSwitch table is null for Node {} ", node);
+                return;
+            }
+
+            for (Table<?> row : openvswitchTable.values()) {
+                populateTunnelEndpoint(node, (Open_vSwitch)row);
+            }
+        } catch (Exception e) {
+            logger.error("Error populating Tunnel Endpoint for Node {} ", node, e);
+        }
+    }
+
+    // Use this later if there is a need to update the tunnel-endpoint dynamically
     public void populateTunnelEndpoint (Node node, String tableName, Table<?> row) {
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
         try {
-            if (tableName.equalsIgnoreCase(getTunnelEndpointConfigTable())) {
-                Map<String, String> configs = ((Open_vSwitch) row).getOther_config();
-                if (configs != null) {
-                    String tunnelEndpoint = configs.get(tunnelEndpointConfigName);
-                    if (tunnelEndpoint != null) {
-                        try {
-                            InetAddress address = InetAddress.getByName(tunnelEndpoint);
-                            addTunnelEndpoint(node, address);
-                            logger.debug("Tunnel Endpoint for Node {} {}", node, address.getHostAddress());
-                        } catch (UnknownHostException e) {
-                            e.printStackTrace();
-                        }
-                    }
-                }
+            if (isInterested(tableName)) {
+                populateTunnelEndpoint(node, (Open_vSwitch)row);
             }
         } catch (Exception e) {
             logger.error("Error populating Tunnel Endpoint for Node {} ", node, e);
index 798600e14d8cd352285889d9df89b09281a93d5f..51404d3ed62e8d65e07d3d052b4225f2cc7d751f 100644 (file)
@@ -1,8 +1,34 @@
 package org.opendaylight.ovsdb.neutron;
 
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
+import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
+import org.opendaylight.ovsdb.lib.notation.UUID;
+import org.opendaylight.ovsdb.lib.table.Bridge;
+import org.opendaylight.ovsdb.lib.table.Interface;
+import org.opendaylight.ovsdb.lib.table.Port;
+import org.opendaylight.ovsdb.lib.table.internal.Table;
+import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
+import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * OpenStack Neutron with the OpenVswitch data plan relies on a typical OVS bridge configurations that
+ * consists of br-int (Integration Bridge), br-tun (Tunnel bridge), br-ex (External bridge).
+ *
+ * In DevStack like setups, the br-tun is not automatically created on the controller nodes.
+ * Hence this class attempts to bring all the nodes to be elibible for OpenStack operations.
+ *
+ */
 public class InternalNetworkManager {
     static final Logger logger = LoggerFactory.getLogger(InternalNetworkManager.class);
 
@@ -13,4 +39,160 @@ public class InternalNetworkManager {
     public static InternalNetworkManager getManager() {
         return internalNetwork;
     }
+
+    public boolean isInternalNetworkNeutronReady(Node node) throws Exception {
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+        Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
+        if (bridgeTable != null) {
+            for (Table<?> row : bridgeTable.values()) {
+                Bridge bridge = (Bridge)row;
+                if (bridge.getName().equals(AdminConfigManager.getManager().getIntegrationBridgeName())) return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isInternalNetworkOverlayReady(Node node) throws Exception {
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+        Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
+        if (bridgeTable != null) {
+            for (Table<?> row : bridgeTable.values()) {
+                Bridge bridge = (Bridge)row;
+                if (bridge.getName().equals(AdminConfigManager.getManager().getTunnelBridgeName())) return true;
+            }
+        }
+        return false;
+    }
+
+    public Status createInternalNetworkForOverlay(Node node) throws Exception {
+        if (!isInternalNetworkNeutronReady(node)) {
+            logger.error("Integration Bridge is not available in Node {}", node);
+            return new Status(StatusCode.NOTACCEPTABLE, "Integration Bridge is not avaialble in Node " + node);
+        }
+        if (isInternalNetworkOverlayReady(node)) {
+            logger.error("Network Overlay Bridge is already present in Node {}", node);
+            return new Status(StatusCode.NOTACCEPTABLE, "Network Overlay Bridge is already present in Node " + node);
+        }
+
+        /*
+         * Lets create this :
+         *
+         * Bridge br-tun
+                Port patch-int
+                    Interface patch-int
+                        type: patch
+                        options: {peer=patch-tun}
+                Port br-tun
+                    Interface br-tun
+                        type: internal
+         */
+
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+        Bridge brTun = new Bridge();
+        brTun.setName(AdminConfigManager.getManager().getTunnelBridgeName());
+        // Create br-tun bridge
+        Status status = ovsdbTable.insertRow(node, Bridge.NAME.getName(), null, brTun);
+        if (!status.isSuccess()) return status;
+        String bridgeUUID = status.getDescription();
+
+        Port port = new Port();
+        port.setName(brTun.getName());
+        status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
+
+        status = addPatchPort(node, bridgeUUID, "patch-int", "patch-tun");
+        if (!status.isSuccess()) return status;
+
+        // Create the corresponding patch-tun port in br-int
+        Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName());
+        for (String brIntUUID : bridges.keySet()) {
+            Bridge brInt = (Bridge) bridges.get(brIntUUID);
+            if (brInt.getName().equalsIgnoreCase(AdminConfigManager.getManager().getIntegrationBridgeName())) {
+                return addPatchPort(node, brIntUUID, "patch-tun", "patch-int");
+            }
+        }
+
+        return status;
+    }
+
+    private Status addPatchPort (Node node, String bridgeUUID, String portName, String patchName) throws Exception {
+        Status status = null;
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+
+        Port patchPort = new Port();
+        patchPort.setName(portName);
+        // Create patch-int port and interface
+        status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, patchPort);
+        if (!status.isSuccess()) return status;
+
+        String patchPortUUID = status.getDescription();
+
+        String interfaceUUID = null;
+        int timeout = 6;
+        while ((interfaceUUID == null) && (timeout > 0)) {
+            patchPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), status.getDescription());
+            OvsDBSet<UUID> interfaces = patchPort.getInterfaces();
+            if (interfaces == null || interfaces.size() == 0) {
+                // Wait for the OVSDB update to sync up the Local cache.
+                Thread.sleep(500);
+                timeout--;
+                continue;
+            }
+            interfaceUUID = interfaces.toArray()[0].toString();
+        }
+
+        if (interfaceUUID == null) {
+            return new Status(StatusCode.INTERNALERROR);
+        }
+
+        Interface tunInterface = new Interface();
+        tunInterface.setType("patch");
+        OvsDBMap<String, String> options = new OvsDBMap<String, String>();
+        options.put("peer", patchName);
+        tunInterface.setOptions(options);
+        status = ovsdbTable.updateRow(node, Interface.NAME.getName(), patchPortUUID, interfaceUUID, tunInterface);
+
+        return status;
+    }
+
+    private void prepareInternalNetwork (NeutronNetwork network, Node node) {
+        // vlan, vxlan, and gre
+        if (network.getProviderNetworkType().equalsIgnoreCase("gre") ||
+                network.getProviderNetworkType().equalsIgnoreCase("vxlan") ||
+                network.getProviderNetworkType().equalsIgnoreCase("vlan")) {
+
+            try {
+                if (!this.isInternalNetworkOverlayReady(node)) {
+                    this.createInternalNetworkForOverlay(node);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else {
+            try {
+                if (!this.isInternalNetworkNeutronReady(node)) {
+                    // TODO : FILL IN
+                    // this.createInternalNetworkForNeutron(node);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void prepareInternalNetwork(NeutronNetwork network) {
+        IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
+        List<Node> nodes = connectionService.getNodes();
+        for (Node node : nodes) {
+            prepareInternalNetwork(network, node);
+        }
+    }
+
+    public void prepareInternalNetwork(Node node) {
+        INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
+        List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks();
+        for (NeutronNetwork network : networks) {
+            prepareInternalNetwork(network, node);
+        }
+    }
+
 }
index 1a9c656a3a464a3e549ca065ff80883f9cf6ff83..62800951a87e7fd85117fd3ee9ad58a1cffd5213 100644 (file)
@@ -59,8 +59,13 @@ public class NetworkHandler extends BaseHandler
             return;
         }
         String networkId = convertNeutronIDToKey(network.getID());
+
+        // Get Network Tenant ready - Containers and Internal Vlans.
         result = TenantNetworkManager.getManager().networkCreated(networkId);
         logger.debug("Neutron Network {} Created with Internal Vlan : {}", network.toString(), result);
+
+        // Get internal network ready for Overlays
+        InternalNetworkManager.getManager().prepareInternalNetwork(network);
     }
 
     /**
index a5574285f41a084a3a013c1e11b42d34877b8569..321c72c48d416532e6ded947cab7876ba4f5df2a 100644 (file)
@@ -11,6 +11,8 @@ public class SouthboundHandler extends BaseHandler implements OVSDBInventoryList
     @Override
     public void nodeAdded(Node node) {
         logger.debug("NODE ADDED {}", node);
+        AdminConfigManager.getManager().populateTunnelEndpoint(node);
+        InternalNetworkManager.getManager().prepareInternalNetwork(node);
     }
 
     @Override
@@ -21,10 +23,13 @@ public class SouthboundHandler extends BaseHandler implements OVSDBInventoryList
     @Override
     public void rowAdded(Node node, String tableName, Table<?> row) {
         logger.debug("ROW ADDED {} , {}", node, row);
-        if (AdminConfigManager.getManager().getTunnelEndpointConfigTable().equalsIgnoreCase(tableName)) {
+        /*
+         * Should we support dynamic update of the Tunnel endpoint configuration ?
+         *
+        if (AdminConfigManager.getManager().isInterested(tableName)) {
             AdminConfigManager.getManager().populateTunnelEndpoint(node, tableName, row);
         }
-
+        */
     }
 
     @Override
index 00deaa6514a15036637a91c62819595e6d86736a..729686e326c725c3041d6fbc11d5479feffdd8ca 100755 (executable)
@@ -208,6 +208,15 @@ public class ConnectionService implements IPluginInConnectionService, IConnectio
         return ovsdbConnections.get(identifier);
     }
 
+    @Override
+    public List<Node> getNodes() {
+        List<Node> nodes = new ArrayList<Node>();
+        for (Connection connection : ovsdbConnections.values()) {
+            nodes.add(connection.getNode());
+        }
+        return nodes;
+    }
+
     @Override
     public void notifyClusterViewChanged() {
     }
@@ -306,6 +315,7 @@ public class ConnectionService implements IPluginInConnectionService, IConnectio
         this.update(connection.getNode(), monitor);
         // With the existing bridges learnt, now it is time to update the OF Controller connections.
         this.updateOFControllers(connection.getNode());
+        inventoryServiceInternal.notifyNodeAdded(connection.getNode());
     }
 
     private void startOvsdbManager() {
index 3aa0294af298b74f69f7751cc60d3bd0c4c98591..68b49fa733094ffde83832a8239a97c18ce71a06 100755 (executable)
@@ -1,5 +1,6 @@
 package org.opendaylight.ovsdb.plugin;
 
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
@@ -8,6 +9,7 @@ import org.opendaylight.controller.sal.core.Node;
 
 public interface IConnectionServiceInternal {
     public Connection getConnection(Node node);
+    public List<Node> getNodes();
     public Node connect(String identifier, Map<ConnectionConstants, String> params);
     public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException;
 }
index 37dbd5a756e5213040c24be7512c3cc5ce86a337..bbbc79638a59c8487c2bcc6f8de0d2d3bbe5d13d 100755 (executable)
@@ -244,6 +244,10 @@ public class InventoryService implements IPluginInInventoryService, InventorySer
     @Override
     public void addNode(Node node, Set<Property> props) {
         addNodeProperty(node, UpdateType.ADDED, props);
+    }
+
+    @Override
+    public void notifyNodeAdded(Node node) {
         OVSDBInventoryListener inventoryListener = (OVSDBInventoryListener)ServiceHelper.getGlobalInstance(OVSDBInventoryListener.class, this);
         if (inventoryListener != null) {
             inventoryListener.nodeAdded(node);
index 9eec13ec251d5ddebc2c59f426815ae73d7019d3..0ad26a64d76128000f2233636dda8d774f48a8ce 100644 (file)
@@ -22,6 +22,7 @@ public interface InventoryServiceInternal {
     public void printCache(Node n);
 
     public void addNode(Node n, Set<Property> props);
+    public void notifyNodeAdded(Node n);
     public void removeNode(Node n);
     public void addNodeProperty(Node node, UpdateType type, Set<Property> props);
 }
\ No newline at end of file