Merge "Identifying and ignoring uninterested SouthBound updates."
[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.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 elibible 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 = 500;
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 boolean isInternalNetworkNeutronReady(Node node) throws Exception {
70         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getIntegrationBridgeName()) != null) {
71             return true;
72         } else {
73             return false;
74         }
75     }
76
77     public boolean isInternalNetworkOverlayReady(Node node) throws Exception {
78         if (this.getInternalBridgeUUID(node, AdminConfigManager.getManager().getTunnelBridgeName()) != null) {
79             return true;
80         } else {
81             return false;
82         }
83     }
84
85     public Status createInternalNetworkForOverlay(Node node) throws Exception {
86         if (!isInternalNetworkNeutronReady(node)) {
87             logger.error("Integration Bridge is not available in Node {}", node);
88             return new Status(StatusCode.NOTACCEPTABLE, "Integration Bridge is not avaialble in Node " + node);
89         }
90         if (isInternalNetworkOverlayReady(node)) {
91             logger.error("Network Overlay Bridge is already present in Node {}", node);
92             return new Status(StatusCode.NOTACCEPTABLE, "Network Overlay Bridge is already present in Node " + node);
93         }
94
95         /*
96          * Lets create this :
97          *
98          * Bridge br-tun
99                 Port patch-int
100                     Interface patch-int
101                         type: patch
102                         options: {peer=patch-tun}
103                 Port br-tun
104                     Interface br-tun
105                         type: internal
106          */
107
108         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
109         Bridge brTun = new Bridge();
110         brTun.setName(AdminConfigManager.getManager().getTunnelBridgeName());
111         // Create br-tun bridge
112         StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Bridge.NAME.getName(), null, brTun);
113         if (!statusWithUuid.isSuccess()) return statusWithUuid;
114         String bridgeUUID = statusWithUuid.getUuid().toString();
115
116         // Set OF Controller
117         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
118         connectionService.setOFController(node, bridgeUUID);
119
120         Port port = new Port();
121         port.setName(brTun.getName());
122         statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, port);
123
124         String patchInt = AdminConfigManager.getManager().getPatchToIntegration();
125         String patchTun = AdminConfigManager.getManager().getPatchToTunnel();
126
127         Status status = addPatchPort(node, bridgeUUID, patchInt, patchTun);
128         if (!status.isSuccess()) return status;
129
130         // Create the corresponding patch-tun port in br-int
131         Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName());
132         for (String brIntUUID : bridges.keySet()) {
133             Bridge brInt = (Bridge) bridges.get(brIntUUID);
134             if (brInt.getName().equalsIgnoreCase(AdminConfigManager.getManager().getIntegrationBridgeName())) {
135                 return addPatchPort(node, brIntUUID, patchTun, patchInt);
136             }
137         }
138
139         return status;
140     }
141
142     private Status addPatchPort (Node node, String bridgeUUID, String portName, String patchName) throws Exception {
143         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
144
145         Port patchPort = new Port();
146         patchPort.setName(portName);
147         // Create patch-int port and interface
148         StatusWithUuid statusWithUuid = ovsdbTable.insertRow(node, Port.NAME.getName(), bridgeUUID, patchPort);
149         if (!statusWithUuid.isSuccess()) return statusWithUuid;
150
151         String patchPortUUID = statusWithUuid.getUuid().toString();
152
153         String interfaceUUID = null;
154         int timeout = 6;
155         while ((interfaceUUID == null) && (timeout > 0)) {
156             patchPort = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), patchPortUUID);
157             OvsDBSet<UUID> interfaces = patchPort.getInterfaces();
158             if (interfaces == null || interfaces.size() == 0) {
159                 // Wait for the OVSDB update to sync up the Local cache.
160                 Thread.sleep(500);
161                 timeout--;
162                 continue;
163             }
164             interfaceUUID = interfaces.toArray()[0].toString();
165         }
166
167         if (interfaceUUID == null) {
168             return new Status(StatusCode.INTERNALERROR);
169         }
170
171         Interface tunInterface = new Interface();
172         tunInterface.setType("patch");
173         OvsDBMap<String, String> options = new OvsDBMap<String, String>();
174         options.put("peer", patchName);
175         tunInterface.setOptions(options);
176         return ovsdbTable.updateRow(node, Interface.NAME.getName(), patchPortUUID, interfaceUUID, tunInterface);
177     }
178
179     private void prepareInternalNetwork (NeutronNetwork network, Node node) {
180         // vlan, vxlan, and gre
181         if (network.getProviderNetworkType().equalsIgnoreCase("gre") ||
182                 network.getProviderNetworkType().equalsIgnoreCase("vxlan") ||
183                 network.getProviderNetworkType().equalsIgnoreCase("vlan")) {
184
185             try {
186                 if (!this.isInternalNetworkOverlayReady(node)) {
187                     this.createInternalNetworkForOverlay(node);
188                 }
189             } catch (Exception e) {
190                 logger.error("Failed to create internal network for overlay on node " + node, e);
191             }
192         } else {
193             try {
194                 if (!this.isInternalNetworkNeutronReady(node)) {
195                     // TODO : FILL IN
196                     // this.createInternalNetworkForNeutron(node);
197                 }
198             } catch (Exception e) {
199                 logger.error("Failed to create internal network for overlay on node " + node, e);
200             }
201         }
202
203         this.initializeOFNormalFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
204         this.initializeLLDPFlowRules(node, AdminConfigManager.getManager().getTunnelBridgeName());
205         this.initializeLLDPFlowRules(node, AdminConfigManager.getManager().getIntegrationBridgeName());
206     }
207
208     private void initializeOFNormalFlowRules(Node node, String bridgeName) {
209         String brIntId = this.getInternalBridgeUUID(node, bridgeName);
210         if (brIntId == null) {
211             logger.error("Failed to initialize Flow Rules for {}", node);
212             return;
213         }
214         try {
215             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
216             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
217             Set<String> dpids = bridge.getDatapath_id();
218             if (dpids == null || dpids.size() ==  0) return;
219             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
220             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
221             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
222                     IForwardingRulesManager.class, "default", this);
223             String flowName = ActionType.HW_PATH.toString();
224             if (frm.getStaticFlow(flowName, ofNode) != null) {
225                 logger.debug("Static Flow {} already programmed in the node {}", flowName, ofNode);
226                 return;
227             }
228             FlowConfig flow = new FlowConfig();
229             flow.setName("IntegrationBridgeNormal");
230             flow.setNode(ofNode);
231             flow.setPriority(NORMAL_PRIORITY+"");
232             List<String> normalAction = new ArrayList<String>();
233             normalAction.add(flowName);
234             flow.setActions(normalAction);
235             Status status = frm.addStaticFlow(flow);
236             logger.debug("Flow Programming Status {} for Flow {} on {} / {}", status, flow, ofNode, node);
237         } catch (Exception e) {
238             logger.error("Failed to initialize Flow Rules for {}", node, e);
239         }
240     }
241
242     private void initializeLLDPFlowRules(Node node, String bridgeName) {
243         String brIntId = this.getInternalBridgeUUID(node, bridgeName);
244         if (brIntId == null) {
245             logger.error("Failed to initialize Flow Rules for {}", node);
246             return;
247         }
248         try {
249             OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
250             Bridge bridge = (Bridge) ovsdbTable.getRow(node, Bridge.NAME.getName(), brIntId);
251             Set<String> dpids = bridge.getDatapath_id();
252             if (dpids == null || dpids.size() ==  0) return;
253             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
254             Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
255             IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
256                     IForwardingRulesManager.class, "default", this);
257             String flowName = "PuntLLDP";
258             if (frm.getStaticFlow(flowName, ofNode) != null) {
259                 logger.debug("Static Flow {} already programmed in the node {}", flowName, ofNode);
260                 return;
261             }
262
263             List<String> puntAction = new ArrayList<String>();
264             puntAction.add(ActionType.CONTROLLER.toString());
265
266             FlowConfig allowLLDP = new FlowConfig();
267             allowLLDP.setInstallInHw(true);
268             allowLLDP.setName(flowName);
269             allowLLDP.setPriority(LLDP_PRIORITY+"");
270             allowLLDP.setNode(ofNode);
271             allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue())
272                     .toUpperCase());
273             allowLLDP.setActions(puntAction);
274             Status status = frm.addStaticFlow(allowLLDP);
275             logger.debug("Flow Programming Status {} for Flow {} on {} / {}", status, allowLLDP, ofNode, node);
276         } catch (Exception e) {
277             logger.error("Failed to initialize Flow Rules for {}", node, e);
278         }
279     }
280
281     public void prepareInternalNetwork(NeutronNetwork network) {
282         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
283         List<Node> nodes = connectionService.getNodes();
284         for (Node node : nodes) {
285             prepareInternalNetwork(network, node);
286         }
287     }
288
289     public void prepareInternalNetwork(Node node) {
290         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
291         List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks();
292         for (NeutronNetwork network : networks) {
293             prepareInternalNetwork(network, node);
294         }
295     }
296
297     public static List safe( List other ) {
298         return other == null ? Collections.EMPTY_LIST : other;
299     }
300 }