OF Flow programming on br-int (NORMAL) and bidirectional br-tun (based on tenant...
[netvirt.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / InternalNetworkManager.java
index 798600e14d8cd352285889d9df89b09281a93d5f..3b45fe1ad42e8781cb4e0bf5b5ec8f2f673037fe 100644 (file)
@@ -1,8 +1,41 @@
 package org.opendaylight.ovsdb.neutron;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
+import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
+import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.HexEncode;
+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 +46,207 @@ public class InternalNetworkManager {
     public static InternalNetworkManager getManager() {
         return internalNetwork;
     }
+
+    public String getInternalBridgeUUID (Node node, String bridgeName) {
+        try {
+            OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+            Map<String, Table<?>> bridgeTable = ovsdbTable.getRows(node, Bridge.NAME.getName());
+            if (bridgeTable == null) return null;
+            for (String key : bridgeTable.keySet()) {
+                Bridge bridge = (Bridge)bridgeTable.get(key);
+                if (bridge.getName().equals(bridgeName)) return key;
+            }
+        } catch (Exception e) {
+            logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
+        }
+        return null;
+    }
+
+    public boolean isInternalNetworkNeutronReady(Node node) throws Exception {
+        if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getIntegrationBridgeName()) != null) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean isInternalNetworkOverlayReady(Node node) throws Exception {
+        if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName()) != null) {
+            return true;
+        } else {
+            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();
+        // Set OF Controller
+        IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
+        connectionService.setOFController(node, bridgeUUID);
+
+        Port port = new Port();
+        port.setName(brTun.getName());
+        status = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
+
+        String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
+        String patchTun = AdminConfigManager.getManager().getPatchToTunnel();
+
+        status = addPatchPort(node, bridgeUUID, patchInt, patchTun);
+        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, patchTun, patchInt);
+            }
+        }
+
+        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(), patchPortUUID);
+            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();
+            }
+        }
+
+        this.initializeFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
+    }
+
+    private void initializeFlowRules(Node node, String bridgeName) {
+        String brIntId = this.getInternalBridgeUUID(node, bridgeName);
+        if (brIntId == null) {
+            logger.error("Failed to initialize Flow Rules for {}", node);
+            return;
+        }
+        try {
+            OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+            Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
+            Set<String> dpids = bridge.getDatapath_id();
+            if (dpids == null || dpids.size() ==  0) return;
+            Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
+            Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
+            IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
+                    IForwardingRulesManager.class, "default", this);
+            FlowConfig flow = new FlowConfig();
+            flow.setName("IntegrationBridgeNormal");
+            flow.setNode(ofNode);
+            flow.setPriority("1");
+            List<String> normalAction = new ArrayList<String>();
+            normalAction.add(ActionType.HW_PATH.toString());
+            flow.setActions(normalAction);
+            Status status = frm.addStaticFlow(flow);
+            logger.debug("Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
+        } catch (Exception e) {
+            logger.error("Failed to initialize Flow Rules for {}", node, e);
+        }
+    }
+
+    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);
+        }
+    }
+
+    public static List safe( List other ) {
+        return other == null ? Collections.EMPTY_LIST : other;
+    }
 }