/* * Copyright (C) 2013 Red Hat, Inc. and others... * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Authors : Madhu Venugopal, Brent Salisbury, Dave Tucker */ package org.opendaylight.ovsdb.neutron; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.opendaylight.controller.containermanager.ContainerConfig; import org.opendaylight.controller.containermanager.ContainerFlowConfig; import org.opendaylight.controller.containermanager.IContainerManager; import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD; import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD; import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork; import org.opendaylight.controller.networkconfig.neutron.NeutronPort; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.utils.HexEncode; import org.opendaylight.controller.sal.utils.ServiceHelper; import org.opendaylight.controller.sal.utils.Status; 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.Open_vSwitch; import org.opendaylight.ovsdb.lib.table.Port; import org.opendaylight.ovsdb.lib.table.internal.Table; import org.opendaylight.ovsdb.neutron.provider.ProviderNetworkManager; import org.opendaylight.ovsdb.plugin.IConnectionServiceInternal; import org.opendaylight.ovsdb.plugin.OVSDBConfigService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TenantNetworkManager { static final Logger logger = LoggerFactory.getLogger(TenantNetworkManager.class); public static final String EXTERNAL_ID_VM_ID = "vm-id"; public static final String EXTERNAL_ID_INTERFACE_ID = "iface-id"; public static final String EXTERNAL_ID_VM_MAC = "attached-mac"; private static TenantNetworkManager tenantHelper = new TenantNetworkManager(); private ConcurrentMap<String, NodeConfiguration> nodeConfigurationCache = new ConcurrentHashMap<>(); private boolean enableContainer = false; private TenantNetworkManager() { String isTenantContainer = System.getProperty("TenantIsContainer"); if (isTenantContainer != null && isTenantContainer.equalsIgnoreCase("true")) { enableContainer = true; } } public static TenantNetworkManager getManager() { return tenantHelper; } public int getInternalVlan(Node node, String networkId) { String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.error("Unable to get UUID for Node {}", node); return 0; } NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid); if (nodeConfiguration == null) { nodeConfiguration = addNodeConfigurationToCache(node); } Integer vlan = nodeConfiguration.getInternalVlan(networkId); if (vlan == null) return 0; return vlan.intValue(); } private NodeConfiguration addNodeConfigurationToCache(Node node) { NodeConfiguration nodeConfiguration = new NodeConfiguration(node); String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.error("Cannot get Node UUID for Node {}", node); return null; } this.nodeConfigurationCache.put(nodeUuid, nodeConfiguration); return nodeConfigurationCache.get(nodeUuid); } public void reclaimTennantNetworkInternalVlan(Node node, String portUUID, NeutronNetwork network) { String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.error("Unable to get UUID for Node {}", node); return; } NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid); // Cache miss if (nodeConfiguration == null) { logger.error("Configuration data unavailable for Node {} ", node); return; } int vlan = nodeConfiguration.reclaimInternalVlan(network.getID()); if (vlan <= 0) { logger.error("Unable to get an internalVlan for Network {}", network); return; } logger.debug("Removed Vlan {} on {}", vlan, portUUID); } public void networkCreated (String networkId) { IConnectionServiceInternal connectionService = (IConnectionServiceInternal)ServiceHelper.getGlobalInstance(IConnectionServiceInternal.class, this); List<Node> nodes = connectionService.getNodes(); for (Node node : nodes) { this.networkCreated(node, networkId); } } private String getNodeUUID(Node node) { String nodeUuid = new String(); OVSDBConfigService ovsdbConfigService = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this); try { Map<String, Table<?>> ovsTable = ovsdbConfigService.getRows(node, Open_vSwitch.NAME.getName()); nodeUuid = (String)ovsTable.keySet().toArray()[0]; } catch (Exception e) { logger.error("Unable to get the Open_vSwitch table for Node {}: {}", node, e); } return nodeUuid; } public int networkCreated (Node node, String networkId) { String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.error("Unable to get UUID for Node {}", node); return 0; } NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid); // Cache miss if (nodeConfiguration == null) { nodeConfiguration = addNodeConfigurationToCache(node); } int internalVlan = nodeConfiguration.assignInternalVlan(networkId); if (enableContainer && internalVlan != 0) { IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this); if (containerManager == null) { logger.error("ContainerManager is null. Failed to create Container for {}", networkId); return 0; } ContainerConfig config = new ContainerConfig(); config.setContainer(BaseHandler.convertNeutronIDToKey(networkId)); Status status = containerManager.addContainer(config); logger.debug("Container Creation Status for {} : {}", networkId, status.toString()); ContainerFlowConfig flowConfig = new ContainerFlowConfig("InternalVlan", internalVlan+"", null, null, null, null, null); List<ContainerFlowConfig> containerFlowConfigs = new ArrayList<ContainerFlowConfig>(); containerFlowConfigs.add(flowConfig); containerManager.addContainerFlows(BaseHandler.convertNeutronIDToKey(networkId), containerFlowConfigs); } return internalVlan; } /** * Are there any TenantNetwork VM present on this Node ? * This method uses Interface Table's external-id field to locate the VM. */ public boolean isTenantNetworkPresentInNode(Node node, String segmentationId) { String networkId = this.getNetworkIdForSegmentationId(segmentationId); if (networkId == null) { logger.debug("Tenant Network not found with Segmenation-id {}",segmentationId); return false; } if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) { String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.debug("Unable to get UUID for Node {}", node); return false; } NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid); // Cache miss if (nodeConfiguration == null) { logger.error("Configuration data unavailable for Node {} ", node); return false; } int internalVlan = nodeConfiguration.getInternalVlan(networkId); if (internalVlan == 0) { logger.debug("No InternalVlan provisioned for Tenant Network {}",networkId); return false; } } OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this); try { /* // Vlan Tag based identification Map<String, Table<?>> portTable = ovsdbTable.getRows(node, Port.NAME.getName()); if (portTable == null) { logger.debug("Port table is null for Node {} ", node); return false; } for (Table<?> row : portTable.values()) { Port port = (Port)row; Set<BigInteger> tags = port.getTag(); if (tags.contains(internalVlan)) { logger.debug("Tenant Network {} with Segmenation-id {} is present in Node {} / Port {}", networkId, segmentationId, node, port); return true; } } */ // External-id based more accurate VM Location identification Map<String, Table<?>> ifTable = ovsdbTable.getRows(node, Interface.NAME.getName()); if (ifTable == null) { logger.debug("Interface table is null for Node {} ", node); return false; } for (Table<?> row : ifTable.values()) { Interface intf = (Interface)row; Map<String, String> externalIds = intf.getExternal_ids(); if (externalIds != null && externalIds.get(EXTERNAL_ID_INTERFACE_ID) != null) { if (this.isInterfacePresentInTenantNetwork(externalIds.get(EXTERNAL_ID_INTERFACE_ID), networkId)) { logger.debug("Tenant Network {} with Segmentation-id {} is present in Node {} / Interface {}", networkId, segmentationId, node, intf); return true; } } } } catch (Exception e) { logger.error("Error while trying to determine if network is present on node", e); return false; } logger.debug("Tenant Network {} with Segmenation-id {} is NOT present in Node {}", networkId, segmentationId, node); return false; } public String getNetworkIdForSegmentationId (String segmentationId) { INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this); List <NeutronNetwork> networks = neutronNetworkService.getAllNetworks(); for (NeutronNetwork network : networks) { if (network.getProviderSegmentationID().equalsIgnoreCase(segmentationId)) return network.getNetworkUUID(); } return null; } private boolean isInterfacePresentInTenantNetwork (String portId, String networkId) { INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this); NeutronPort neutronPort = neutronPortService.getPort(portId); if (neutronPort != null && neutronPort.getNetworkUUID().equalsIgnoreCase(networkId)) return true; return false; } public NeutronNetwork getTenantNetworkForInterface (Interface intf) { logger.trace("getTenantNetworkForInterface for {}", intf); if (intf == null) return null; Map<String, String> externalIds = intf.getExternal_ids(); logger.trace("externalIds {}", externalIds); if (externalIds == null) return null; String neutronPortId = externalIds.get(EXTERNAL_ID_INTERFACE_ID); if (neutronPortId == null) return null; INeutronPortCRUD neutronPortService = (INeutronPortCRUD)ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this); NeutronPort neutronPort = neutronPortService.getPort(neutronPortId); logger.trace("neutronPort {}", neutronPort); if (neutronPort == null) return null; INeutronNetworkCRUD neutronNetworkService = (INeutronNetworkCRUD)ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this); NeutronNetwork neutronNetwork = neutronNetworkService.getNetwork(neutronPort.getNetworkUUID()); logger.debug("{} mappped to {}", intf, neutronNetwork); return neutronNetwork; } public void programTenantNetworkInternalVlan(Node node, String portUUID, NeutronNetwork network) { String nodeUuid = getNodeUUID(node); if (nodeUuid == null) { logger.error("Unable to get UUID for Node {}", node); return; } NodeConfiguration nodeConfiguration = nodeConfigurationCache.get(nodeUuid); // Cache miss if (nodeConfiguration == null) { logger.error("Configuration data unavailable for Node {} ", node); return; } int vlan = nodeConfiguration.getInternalVlan(network.getID()); logger.debug("Programming Vlan {} on {}", vlan, portUUID); if (vlan <= 0) { logger.error("Unable to get an internalVlan for Network {}", network); return; } OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this); Port port = new Port(); OvsDBSet<BigInteger> tags = new OvsDBSet<BigInteger>(); tags.add(BigInteger.valueOf(vlan)); port.setTag(tags); ovsdbTable.updateRow(node, Port.NAME.getName(), null, portUUID, port); if (enableContainer) this.addPortToTenantNetworkContainer(node, portUUID, network); } private void addPortToTenantNetworkContainer(Node node, String portUUID, NeutronNetwork network) { IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this); if (containerManager == null) { logger.error("ContainerManager is not accessible"); return; } OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this); try { Port port = (Port)ovsdbTable.getRow(node, Port.NAME.getName(), portUUID); if (port == null) { logger.trace("Unable to identify Port with UUID {}", portUUID); return; } Set<UUID> interfaces = port.getInterfaces(); if (interfaces == null) { logger.trace("No interfaces available to fetch the OF Port"); return; } Bridge bridge = this.getBridgeIdForPort(node, portUUID); if (bridge == null) { logger.debug("Unable to spot Bridge for Port {} in node {}", port, node); return; } Set<String> dpids = bridge.getDatapath_id(); if (dpids == null || dpids.size() == 0) return; Long dpidLong = Long.valueOf(HexEncode.stringToLong((String)dpids.toArray()[0])); for (UUID intfUUID : interfaces) { Interface intf = (Interface)ovsdbTable.getRow(node, Interface.NAME.getName(), intfUUID.toString()); if (intf == null) continue; Set<BigInteger> of_ports = intf.getOfport(); if (of_ports == null) continue; for (BigInteger of_port : of_ports) { ContainerConfig config = new ContainerConfig(); config.setContainer(BaseHandler.convertNeutronIDToKey(network.getID())); logger.debug("Adding Port {} to Container : {}", port.toString(), config.getContainer()); List<String> ncList = new ArrayList<String>(); Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong); NodeConnector nc = NodeConnector.fromStringNoNode(Node.NodeIDType.OPENFLOW.toString(), Long.valueOf(of_port.longValue()).intValue()+"", ofNode); ncList.add(nc.toString()); config.addNodeConnectors(ncList); Status status = containerManager.addContainerEntry(BaseHandler.convertNeutronIDToKey(network.getID()), ncList); if (!status.isSuccess()) { logger.error(" Failed {} : to add port {} to container - {}", status, nc, network.getID()); } else { logger.error(" Successfully added port {} to container - {}", nc, network.getID()); } } } } catch (Exception e) { logger.error("Exception in addPortToTenantNetworkContainer", e); } } private Bridge getBridgeIdForPort (Node node, String uuid) { OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this); try { Map<String, Table<?>> bridges = ovsdbTable.getRows(node, Bridge.NAME.getName()); if (bridges == null) return null; for (String bridgeUUID : bridges.keySet()) { Bridge bridge = (Bridge)bridges.get(bridgeUUID); Set<UUID> portUUIDs = bridge.getPorts(); logger.trace("Scanning Bridge {} to identify Port : {} ",bridge, uuid); for (UUID portUUID : portUUIDs) { if (portUUID.toString().equalsIgnoreCase(uuid)) { logger.trace("Found Port {} -> ", uuid, bridgeUUID); return bridge; } } } } catch (Exception e) { logger.debug("Failed to get BridgeId port {} in Node {}", uuid, node); } return null; } public void networkDeleted(String id) { if (!enableContainer) return; IContainerManager containerManager = (IContainerManager)ServiceHelper.getGlobalInstance(IContainerManager.class, this); if (containerManager == null) { logger.error("ContainerManager is not accessible"); return; } String networkID = BaseHandler.convertNeutronIDToKey(id); ContainerConfig config = new ContainerConfig(); config.setContainer(networkID); containerManager.removeContainer(config); } }