Replace tenantVlanMap with a per Node cache
[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 networkCreated (String networkId) {
95         IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this);
96         List<Node> nodes = connectionService.getNodes();
97
98         for (Node node : nodes) {
99             this.networkCreated(node, networkId);
100         }
101
102     }
103
104     private String getNodeUUID(Node node) {
105         String nodeUuid = new String();
106         OVSDBConfigService ovsdbConfigService = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
107         try {
108             Map<String, Table<?>> ovsTable = ovsdbConfigService.getRows(node, Open_vSwitch.NAME.getName());
109             nodeUuid = (String)ovsTable.keySet().toArray()[0];
110         }
111         catch (Exception e) {
112             logger.error("Unable to get the Open_vSwitch table for Node {}: {}", node, e);
113         }
114
115         return nodeUuid;
116     }
117
118     public int networkCreated (Node node, String networkId) {
119         String nodeUuid = getNodeUUID(node);
120         if (nodeUuid == null) {
121             logger.error("Unable to get UUID for Node {}", node);
122             return 0;
123         }
124
125         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
126
127         // Cache miss
128         if (nodeConfiguration == null)
129         {
130             nodeConfiguration = addNodeConfigurationToCache(node);
131         }
132
133         int internalVlan = nodeConfiguration.assignInternalVlan(networkId);
134         if (enableContainer && internalVlan != 0) {
135             IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
136             if (containerManager == null) {
137                 logger.error("ContainerManager is null. Failed to create Container for {}", networkId);
138                 return 0;
139             }
140
141             ContainerConfig config = new ContainerConfig();
142             config.setContainer(BaseHandler.convertNeutronIDToKey(networkId));
143             Status status = containerManager.addContainer(config);
144             logger.debug("Container Creation Status for {} : {}", networkId, status.toString());
145
146             ContainerFlowConfig flowConfig = new ContainerFlowConfig("InternalVlan", internalVlan+"",
147                     null, null, null, null, null);
148             List<ContainerFlowConfig> containerFlowConfigs = new ArrayList<ContainerFlowConfig>();
149             containerFlowConfigs.add(flowConfig);
150             containerManager.addContainerFlows(BaseHandler.convertNeutronIDToKey(networkId), containerFlowConfigs);
151         }
152         return internalVlan;
153     }
154
155     /**
156      * Are there any TenantNetwork VM present on this Node ?
157      * This method uses Interface Table's external-id field to locate the VM.
158      */
159     public boolean isTenantNetworkPresentInNode(Node node, String segmentationId) {
160         String networkId = this.getNetworkIdForSegmentationId(segmentationId);
161         if (networkId == null) {
162             logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId);
163             return false;
164         }
165         if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
166             String nodeUuid = getNodeUUID(node);
167             if (nodeUuid == null) {
168                 logger.debug("Unable to get UUID for Node {}", node);
169                 return false;
170             }
171
172             NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
173
174             // Cache miss
175             if (nodeConfiguration == null)
176             {
177                 logger.error("Configuration data unavailable for Node {} ", node);
178                 return false;
179             }
180
181             int internalVlan = nodeConfiguration.getInternalVlan(networkId);
182             if (internalVlan == 0) {
183                 logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId);
184                 return false;
185             }
186         }
187         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
188         try {
189             /*
190             // Vlan Tag based identification
191             Map<String, Table<?>> portTable = ovsdbTable.getRows(node, Port.NAME.getName());
192             if (portTable == null) {
193                 logger.debug("Port table is null for Node {} ", node);
194                 return false;
195             }
196
197             for (Table<?> row : portTable.values()) {
198                 Port port = (Port)row;
199                 Set<BigInteger> tags = port.getTag();
200                 if (tags.contains(internalVlan)) {
201                     logger.debug("Tenant Network {} with Segmenation-id {} is present in Node {} / Port {}",
202                                   networkId, segmentationId, node, port);
203                     return true;
204                 }
205             }
206              */
207             // External-id based more accurate VM Location identification
208             Map<String, Table<?>> ifTable = ovsdbTable.getRows(node, Interface.NAME.getName());
209             if (ifTable == null) {
210                 logger.debug("Interface table is null for Node {} ", node);
211                 return false;
212             }
213
214             for (Table<?> row : ifTable.values()) {
215                 Interface intf = (Interface)row;
216                 Map<String, String> externalIds = intf.getExternal_ids();
217                 if (externalIds != null && externalIds.get(EXTERNAL_ID_INTERFACE_ID) != null) {
218                     if (this.isInterfacePresentInTenantNetwork(externalIds.get(EXTERNAL_ID_INTERFACE_ID), networkId)) {
219                         logger.debug("Tenant Network {} with Segmentation-id {} is present in Node {} / Interface {}",
220                                       networkId, segmentationId, node, intf);
221                         return true;
222                     }
223                 }
224             }
225
226         } catch (Exception e) {
227             logger.error("Error while trying to determine if network is present on node", e);
228             return false;
229         }
230
231         logger.debug("Tenant Network {} with Segmenation-id {} is NOT present in Node {}",
232                 networkId, segmentationId, node);
233
234         return false;
235     }
236
237     public String getNetworkIdForSegmentationId (String segmentationId) {
238         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
239         List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks();
240         for (NeutronNetwork network : networks) {
241             if (network.getProviderSegmentationID().equalsIgnoreCase(segmentationId)) return network.getNetworkUUID();
242         }
243         return null;
244     }
245
246     private boolean isInterfacePresentInTenantNetwork (String portId, String networkId) {
247         INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this);
248         NeutronPort neutronPort = neutronPortService.getPort(portId);
249         if (neutronPort != null && neutronPort.getNetworkUUID().equalsIgnoreCase(networkId)) return true;
250         return false;
251     }
252
253     public NeutronNetwork getTenantNetworkForInterface (Interface intf) {
254         logger.trace("getTenantNetworkForInterface for {}", intf);
255         if (intf == null) return null;
256         Map<String, String> externalIds = intf.getExternal_ids();
257         logger.trace("externalIds {}", externalIds);
258         if (externalIds == null) return null;
259         String neutronPortId = externalIds.get(EXTERNAL_ID_INTERFACE_ID);
260         if (neutronPortId == null) return null;
261         INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this);
262         NeutronPort neutronPort = neutronPortService.getPort(neutronPortId);
263         logger.trace("neutronPort {}", neutronPort);
264         if (neutronPort == null) return null;
265         INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
266         NeutronNetwork neutronNetwork = neutronNetworkService.getNetwork(neutronPort.getNetworkUUID());
267         logger.debug("{} mappped to {}", intf, neutronNetwork);
268         return neutronNetwork;
269     }
270
271     public void programTenantNetworkInternalVlan(Node node, String portUUID, NeutronNetwork network) {
272
273         String nodeUuid = getNodeUUID(node);
274         if (nodeUuid == null) {
275             logger.error("Unable to get UUID for Node {}", node);
276             return;
277         }
278
279         NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid);
280
281         // Cache miss
282         if (nodeConfiguration == null)
283         {
284             logger.error("Configuration data unavailable for Node {} ", node);
285             return;
286         }
287
288         int vlan = nodeConfiguration.getInternalVlan(network.getID());
289         logger.debug("Programming Vlan {} on {}", vlan, portUUID);
290         if (vlan <= 0) {
291             logger.error("Unable to get an internalVlan for Network {}", network);
292             return;
293         }
294         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
295         Port port = new Port();
296         OvsDBSet<BigInteger> tags = new OvsDBSet<BigInteger>();
297         tags.add(BigInteger.valueOf(vlan));
298         port.setTag(tags);
299         ovsdbTable.updateRow(node, Port.NAME.getName(), null, portUUID, port);
300         if (enableContainer) this.addPortToTenantNetworkContainer(node, portUUID, network);
301     }
302
303     private void addPortToTenantNetworkContainer(Node node, String portUUID, NeutronNetwork network) {
304         IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
305         if (containerManager == null) {
306             logger.error("ContainerManager is not accessible");
307             return;
308         }
309         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
310         try {
311             Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID);
312             if (port == null) {
313                 logger.trace("Unable to identify Port with UUID {}", portUUID);
314                 return;
315             }
316             Set<UUID> interfaces = port.getInterfaces();
317             if (interfaces == null) {
318                 logger.trace("No interfaces available to fetch the OF Port");
319                 return;
320             }
321             Bridge bridge = this.getBridgeIdForPort(node, portUUID);
322             if (bridge == null) {
323                 logger.debug("Unable to spot Bridge for Port {} in node {}", port, node);
324                 return;
325             }
326             Set<String> dpids = bridge.getDatapath_id();
327             if (dpids == null || dpids.size() ==  0) return;
328             Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0]));
329
330             for (UUID intfUUID : interfaces) {
331                 Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), intfUUID.toString());
332                 if (intf == null) continue;
333                 Set<BigInteger> of_ports = intf.getOfport();
334                 if (of_ports == null) continue;
335                 for (BigInteger of_port : of_ports) {
336                     ContainerConfig config = new ContainerConfig();
337                     config.setContainer(BaseHandler.convertNeutronIDToKey(network.getID()));
338                     logger.debug("Adding Port {} to Container : {}", port.toString(), config.getContainer());
339                     List<String> ncList = new ArrayList<String>();
340                     Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
341                     NodeConnector nc = NodeConnector.fromStringNoNode(Node.NodeIDType.OPENFLOW.toString(),
342                                                                       Long.valueOf(of_port.longValue()).intValue()+"",
343                                                                       ofNode);
344                     ncList.add(nc.toString());
345                     config.addNodeConnectors(ncList);
346
347                     Status status = containerManager.addContainerEntry(BaseHandler.convertNeutronIDToKey(network.getID()), ncList);
348
349                     if (!status.isSuccess()) {
350                         logger.error(" Failed {} : to add port {} to container - {}",
351                                 status, nc, network.getID());
352                     } else {
353                         logger.error(" Successfully added port {} to container - {}",
354                                        nc, network.getID());
355                     }
356                 }
357             }
358         } catch (Exception e) {
359             logger.error("Exception in addPortToTenantNetworkContainer", e);
360         }
361     }
362
363     private Bridge getBridgeIdForPort (Node node, String uuid) {
364         OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
365         try {
366             Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName());
367             if (bridges == null) return null;
368             for (String bridgeUUID : bridges.keySet()) {
369                 Bridge bridge = (Bridge)bridges.get(bridgeUUID);
370                 Set<UUID> portUUIDs = bridge.getPorts();
371                 logger.trace("Scanning Bridge {} to identify Port : {} ",bridge, uuid);
372                 for (UUID portUUID : portUUIDs) {
373                     if (portUUID.toString().equalsIgnoreCase(uuid)) {
374                         logger.trace("Found Port {} -> ", uuid, bridgeUUID);
375                         return bridge;
376                     }
377                 }
378             }
379         } catch (Exception e) {
380             logger.debug("Failed to get BridgeId port {} in Node {}", uuid, node);
381         }
382         return null;
383     }
384
385     public void networkDeleted(String id) {
386         if (!enableContainer) return;
387
388         IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this);
389         if (containerManager == null) {
390             logger.error("ContainerManager is not accessible");
391             return;
392         }
393
394         String networkID = BaseHandler.convertNeutronIDToKey(id);
395         ContainerConfig config = new ContainerConfig();
396         config.setContainer(networkID);
397         containerManager.removeContainer(config);
398     }
399
400 }