0ce6a400f238173713e26450f4d5ebbbbafa1af9
[ovsdb.git] / neutron / src / main / java / org / opendaylight / ovsdb / neutron / TenantNetworkManager.java
1 /*
2  * Copyright (C) 2013 Red Hat, Inc. and others...
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, Dave Tucker
9  */
10 package org.opendaylight.ovsdb.neutron;
11
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19
20 import org.opendaylight.controller.containermanager.ContainerConfig;
21 import org.opendaylight.controller.containermanager.ContainerFlowConfig;
22 import org.opendaylight.controller.containermanager.IContainerManager;
23 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
24 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
25 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
26 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
27 import org.opendaylight.controller.sal.core.Node;
28 import org.opendaylight.controller.sal.core.NodeConnector;
29 import org.opendaylight.controller.sal.utils.HexEncode;
30 import org.opendaylight.controller.sal.utils.ServiceHelper;
31 import org.opendaylight.controller.sal.utils.Status;
32 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
33 import org.opendaylight.ovsdb.lib.notation.UUID;
34 import org.opendaylight.ovsdb.lib.table.Bridge;
35 import org.opendaylight.ovsdb.lib.table.Interface;
36 import org.opendaylight.ovsdb.lib.table.Open_vSwitch;
37 import org.opendaylight.ovsdb.lib.table.Port;
38 import org.opendaylight.ovsdb.lib.table.internal.Table;
39 import org.opendaylight.ovsdb.neutron.provider.ProviderNetworkManager;
40 import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal;
41 import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class TenantNetworkManager {
46     static final Logger logger = LoggerFactory.getLogger(TenantNetworkManager.class);
47
48     public static final String EXTERNAL_ID_VM_ID = "vm-id";
49     public static final String EXTERNAL_ID_INTERFACE_ID = "iface-id";
50     public static final String EXTERNAL_ID_VM_MAC = "attached-mac";
51     private static TenantNetworkManager tenantHelper = new TenantNetworkManager();
52     private ConcurrentMap<String, NodeConfiguration> nodeConfigurationCache = new ConcurrentHashMap<>();
53
54     private boolean enableContainer = false;
55     private TenantNetworkManager() {
56         String isTenantContainer = System.getProperty("TenantIsContainer");
57         if (isTenantContainer != null && isTenantContainer.equalsIgnoreCase("true")) {
58             enableContainer =  true;
59         }
60     }
61
62     public static TenantNetworkManager getManager() {
63         return tenantHelper;
64     }
65
66     public int getInternalVlan(Node node, String networkId) {
67         String nodeUuid = getNodeUUID(node);
68         if (nodeUuid == null) {
69             logger.error("Unable to get UUID for Node {}", node);
70             return 0;
71         }
72
73         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
74
75         if (nodeConfiguration == null) {
76             nodeConfiguration = addNodeConfigurationToCache(node);
77         }
78         Integer vlan = nodeConfiguration.getInternalVlan(networkId);
79         if (vlan == null) return 0;
80         return vlan.intValue();
81     }
82
83     private NodeConfiguration addNodeConfigurationToCache(Node node) {
84         NodeConfiguration nodeConfiguration = new NodeConfiguration(node);
85         String nodeUuid = getNodeUUID(node);
86         if (nodeUuid == null) {
87             logger.error("Cannot get Node UUID for Node {}", node);
88             return null;
89         }
90         this.nodeConfigurationCache.put(nodeUuid, nodeConfiguration);
91         return nodeConfigurationCache.get(nodeUuid);
92     }
93
94     public void reclaimTennantNetworkInternalVlan(Node node, String portUUID, NeutronNetwork network) {
95         String nodeUuid = getNodeUUID(node);
96         if (nodeUuid == null) {
97             logger.error("Unable to get UUID for Node {}", node);
98             return;
99         }
100
101         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
102
103         // Cache miss
104         if (nodeConfiguration == null)
105         {
106             logger.error("Configuration data unavailable for Node {} ", node);
107             return;
108         }
109
110         int vlan = nodeConfiguration.reclaimInternalVlan(network.getID());
111         if (vlan <= 0) {
112             logger.error("Unable to get an internalVlan for Network {}", network);
113             return;
114         }
115         logger.debug("Removed Vlan {} on {}", vlan, portUUID);
116     }
117
118     public void networkCreated (String networkId) {
119         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
120         List<Node> nodes = connectionService.getNodes();
121
122         for (Node node : nodes) {
123             this.networkCreated(node, networkId);
124         }
125
126     }
127
128     private String getNodeUUID(Node node) {
129         String nodeUuid = new String();
130         OVSDBConfigService ovsdbConfigService = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
131         try {
132             Map<String, Table<?>> ovsTable = ovsdbConfigService.getRows(node, Open_vSwitch.NAME.getName());
133             nodeUuid = (String)ovsTable.keySet().toArray()[0];
134         }
135         catch (Exception e) {
136             logger.error("Unable to get the Open_vSwitch table for Node {}: {}", node, e);
137         }
138
139         return nodeUuid;
140     }
141
142     public int networkCreated (Node node, String networkId) {
143         String nodeUuid = getNodeUUID(node);
144         if (nodeUuid == null) {
145             logger.error("Unable to get UUID for Node {}", node);
146             return 0;
147         }
148
149         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
150
151         // Cache miss
152         if (nodeConfiguration == null)
153         {
154             nodeConfiguration = addNodeConfigurationToCache(node);
155         }
156
157         int internalVlan = nodeConfiguration.assignInternalVlan(networkId);
158         if (enableContainer && internalVlan != 0) {
159             IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
160             if (containerManager == null) {
161                 logger.error("ContainerManager is null. Failed to create Container for {}", networkId);
162                 return 0;
163             }
164
165             ContainerConfig config = new ContainerConfig();
166             config.setContainer(BaseHandler.convertNeutronIDToKey(networkId));
167             Status status = containerManager.addContainer(config);
168             logger.debug("Container Creation Status for {} : {}", networkId, status.toString());
169
170             ContainerFlowConfig flowConfig = new ContainerFlowConfig("InternalVlan", internalVlan+"",
171                     null, null, null, null, null);
172             List<ContainerFlowConfig> containerFlowConfigs = new ArrayList<ContainerFlowConfig>();
173             containerFlowConfigs.add(flowConfig);
174             containerManager.addContainerFlows(BaseHandler.convertNeutronIDToKey(networkId), containerFlowConfigs);
175         }
176         return internalVlan;
177     }
178
179     /**
180      * Are there any TenantNetwork VM present on this Node ?
181      * This method uses Interface Table's external-id field to locate the VM.
182      */
183     public boolean isTenantNetworkPresentInNode(Node node, String segmentationId) {
184         String networkId = this.getNetworkIdForSegmentationId(segmentationId);
185         if (networkId == null) {
186             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
187             return false;
188         }
189         if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
190             String nodeUuid = getNodeUUID(node);
191             if (nodeUuid == null) {
192                 logger.debug("Unable to get UUID for Node {}", node);
193                 return false;
194             }
195
196             NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
197
198             // Cache miss
199             if (nodeConfiguration == null)
200             {
201                 logger.error("Configuration data unavailable for Node {} ", node);
202                 return false;
203             }
204
205             int internalVlan = nodeConfiguration.getInternalVlan(networkId);
206             if (internalVlan == 0) {
207                 logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
208                 return false;
209             }
210         }
211         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
212         try {
213             /*
214             // Vlan Tag based identification
215             Map<String, Table<?>> portTable = ovsdbTable.getRows(node, Port.NAME.getName());
216             if (portTable == null) {
217                 logger.debug("Port table is null for Node {} ", node);
218                 return false;
219             }
220
221             for (Table<?> row : portTable.values()) {
222                 Port port = (Port)row;
223                 Set<BigInteger> tags = port.getTag();
224                 if (tags.contains(internalVlan)) {
225                     logger.debug("Tenant Network {} with Segmenation-id {} is present in Node {} / Port {}",
226                                   networkId, segmentationId, node, port);
227                     return true;
228                 }
229             }
230              */
231             // External-id based more accurate VM Location identification
232             Map<String, Table<?>> ifTable = ovsdbTable.getRows(node, Interface.NAME.getName());
233             if (ifTable == null) {
234                 logger.debug("Interface table is null for Node {} ", node);
235                 return false;
236             }
237
238             for (Table<?> row : ifTable.values()) {
239                 Interface intf = (Interface)row;
240                 Map<String, String> externalIds = intf.getExternal_ids();
241                 if (externalIds != null && externalIds.get(EXTERNAL_ID_INTERFACE_ID) != null) {
242                     if (this.isInterfacePresentInTenantNetwork(externalIds.get(EXTERNAL_ID_INTERFACE_ID), networkId)) {
243                         logger.debug("Tenant Network {} with Segmentation-id {} is present in Node {} / Interface {}",
244                                       networkId, segmentationId, node, intf);
245                         return true;
246                     }
247                 }
248             }
249
250         } catch (Exception e) {
251             logger.error("Error while trying to determine if network is present on node", e);
252             return false;
253         }
254
255         logger.debug("Tenant Network {} with Segmenation-id {} is NOT present in Node {}",
256                 networkId, segmentationId, node);
257
258         return false;
259     }
260
261     public String getNetworkIdForSegmentationId (String segmentationId) {
262         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
263         List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks();
264         for (NeutronNetwork network : networks) {
265             if (network.getProviderSegmentationID().equalsIgnoreCase(segmentationId)) return network.getNetworkUUID();
266         }
267         return null;
268     }
269
270     private boolean isInterfacePresentInTenantNetwork (String portId, String networkId) {
271         INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this);
272         NeutronPort neutronPort = neutronPortService.getPort(portId);
273         if (neutronPort != null && neutronPort.getNetworkUUID().equalsIgnoreCase(networkId)) return true;
274         return false;
275     }
276
277     public NeutronNetwork getTenantNetworkForInterface (Interface intf) {
278         logger.trace("getTenantNetworkForInterface for {}", intf);
279         if (intf == null) return null;
280         Map<String, String> externalIds = intf.getExternal_ids();
281         logger.trace("externalIds {}", externalIds);
282         if (externalIds == null) return null;
283         String neutronPortId = externalIds.get(EXTERNAL_ID_INTERFACE_ID);
284         if (neutronPortId == null) return null;
285         INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this);
286         NeutronPort neutronPort = neutronPortService.getPort(neutronPortId);
287         logger.trace("neutronPort {}", neutronPort);
288         if (neutronPort == null) return null;
289         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
290         NeutronNetwork neutronNetwork = neutronNetworkService.getNetwork(neutronPort.getNetworkUUID());
291         logger.debug("{} mappped to {}", intf, neutronNetwork);
292         return neutronNetwork;
293     }
294
295     public void programTenantNetworkInternalVlan(Node node, String portUUID, NeutronNetwork network) {
296
297         String nodeUuid = getNodeUUID(node);
298         if (nodeUuid == null) {
299             logger.error("Unable to get UUID for Node {}", node);
300             return;
301         }
302
303         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
304
305         // Cache miss
306         if (nodeConfiguration == null)
307         {
308             logger.error("Configuration data unavailable for Node {} ", node);
309             return;
310         }
311
312         int vlan = nodeConfiguration.getInternalVlan(network.getID());
313         logger.debug("Programming Vlan {} on {}", vlan, portUUID);
314         if (vlan <= 0) {
315             logger.error("Unable to get an internalVlan for Network {}", network);
316             return;
317         }
318         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
319         Port port = new Port();
320         OvsDBSet<BigInteger> tags = new OvsDBSet<BigInteger>();
321         tags.add(BigInteger.valueOf(vlan));
322         port.setTag(tags);
323         ovsdbTable.updateRow(node, Port.NAME.getName(), null, portUUID, port);
324         if (enableContainer) this.addPortToTenantNetworkContainer(node, portUUID, network);
325     }
326
327     private void addPortToTenantNetworkContainer(Node node, String portUUID, NeutronNetwork network) {
328         IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
329         if (containerManager == null) {
330             logger.error("ContainerManager is not accessible");
331             return;
332         }
333         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
334         try {
335             Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID);
336             if (port == null) {
337                 logger.trace("Unable to identify Port with UUID {}", portUUID);
338                 return;
339             }
340             Set<UUID> interfaces = port.getInterfaces();
341             if (interfaces == null) {
342                 logger.trace("No interfaces available to fetch the OF Port");
343                 return;
344             }
345             Bridge bridge = this.getBridgeIdForPort(node, portUUID);
346             if (bridge == null) {
347                 logger.debug("Unable to spot Bridge for Port {} in node {}", port, node);
348                 return;
349             }
350             Set<String> dpids = bridge.getDatapath_id();
351             if (dpids == null || dpids.size() ==  0) return;
352             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
353
354             for (UUID intfUUID : interfaces) {
355                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), intfUUID.toString());
356                 if (intf == null) continue;
357                 Set<BigInteger> of_ports = intf.getOfport();
358                 if (of_ports == null) continue;
359                 for (BigInteger of_port : of_ports) {
360                     ContainerConfig config = new ContainerConfig();
361                     config.setContainer(BaseHandler.convertNeutronIDToKey(network.getID()));
362                     logger.debug("Adding Port {} to Container : {}", port.toString(), config.getContainer());
363                     List<String> ncList = new ArrayList<String>();
364                     Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
365                     NodeConnector nc = NodeConnector.fromStringNoNode(Node.NodeIDType.OPENFLOW.toString(),
366                                                                       Long.valueOf(of_port.longValue()).intValue()+"",
367                                                                       ofNode);
368                     ncList.add(nc.toString());
369                     config.addNodeConnectors(ncList);
370
371                     Status status = containerManager.addContainerEntry(BaseHandler.convertNeutronIDToKey(network.getID()), ncList);
372
373                     if (!status.isSuccess()) {
374                         logger.error(" Failed {} : to add port {} to container - {}",
375                                 status, nc, network.getID());
376                     } else {
377                         logger.error(" Successfully added port {} to container - {}",
378                                        nc, network.getID());
379                     }
380                 }
381             }
382         } catch (Exception e) {
383             logger.error("Exception in addPortToTenantNetworkContainer", e);
384         }
385     }
386
387     private Bridge getBridgeIdForPort (Node node, String uuid) {
388         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
389         try {
390             Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName());
391             if (bridges == null) return null;
392             for (String bridgeUUID : bridges.keySet()) {
393                 Bridge bridge = (Bridge)bridges.get(bridgeUUID);
394                 Set<UUID> portUUIDs = bridge.getPorts();
395                 logger.trace("Scanning Bridge {} to identify Port : {} ",bridge, uuid);
396                 for (UUID portUUID : portUUIDs) {
397                     if (portUUID.toString().equalsIgnoreCase(uuid)) {
398                         logger.trace("Found Port {} -> ", uuid, bridgeUUID);
399                         return bridge;
400                     }
401                 }
402             }
403         } catch (Exception e) {
404             logger.debug("Failed to get BridgeId port {} in Node {}", uuid, node);
405         }
406         return null;
407     }
408
409     public void networkDeleted(String id) {
410         if (!enableContainer) return;
411
412         IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
413         if (containerManager == null) {
414             logger.error("ContainerManager is not accessible");
415             return;
416         }
417
418         String networkID = BaseHandler.convertNeutronIDToKey(id);
419         ContainerConfig config = new ContainerConfig();
420         config.setContainer(networkID);
421         containerManager.removeContainer(config);
422     }
423
424 }