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