/* * Copyright (c) 2013 Big Switch Networks, Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.sdnplatform.netvirt.web; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openflow.protocol.OFMatch; import org.openflow.protocol.statistics.OFFlowStatisticsReply; import org.openflow.util.HexString; import org.restlet.resource.Get; import org.sdnplatform.core.annotations.LogMessageCategory; import org.sdnplatform.core.annotations.LogMessageDoc; import org.sdnplatform.core.web.AllSwitchStatisticsResource; import org.sdnplatform.devicemanager.IDevice; import org.sdnplatform.devicemanager.IDeviceService; import org.sdnplatform.netvirt.core.VNS; import org.sdnplatform.netvirt.core.VNSInterface; import org.sdnplatform.netvirt.manager.INetVirtManagerService; import org.sdnplatform.packet.Ethernet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements REST API to get flows categorized on NetVirt * @author subrata * @param netVirt, switch are optional parameters, by default it gets flows for all NetVirtes from all switches * */ @LogMessageCategory("Network Virtualization") public class NetVirtFlowResource extends AllSwitchStatisticsResource { protected static Logger log = LoggerFactory.getLogger(ExplainResource.class); public class OFFlowStatsReplywithDpid { String dpid; OFFlowStatisticsReply flowEntry; public String getDpid() { return dpid; } public void setDpid(String dpid) { this.dpid = dpid; } public OFFlowStatisticsReply getFlowEntry() { return flowEntry; } public void setFlowEntry(OFFlowStatisticsReply flowEntry) { this.flowEntry = flowEntry; } } public class PerNetVirtFlowOutput { protected int flowCount=1; protected List<OFFlowStatsReplywithDpid> flowList; public int getFlowCount() { return flowCount; } public void setFlowCount(int flowCount) { this.flowCount = flowCount; } public List<OFFlowStatsReplywithDpid> getFlowList() { return flowList; } public void setFlowList(List<OFFlowStatsReplywithDpid> flowList) { this.flowList = flowList; } } // This is the output structure of the JSON return object public class MultiNetVirtFlowOutput { int netVirtCount=0; // Key = netVirt name (String) protected HashMap<String, PerNetVirtFlowOutput> netVirtFlowMap = new HashMap<String, PerNetVirtFlowOutput>(); protected HashMap<String, String> netVirtAddressSpaceMap = new HashMap<String, String>(); public int getNetVirtCount() { return netVirtCount; } public void setNetVirtCount(int netVirtCount) { this.netVirtCount = netVirtCount; } public HashMap<String, PerNetVirtFlowOutput> getNetVirtFlowMap() { return netVirtFlowMap; } public void setNetVirtFlowMap(HashMap<String, PerNetVirtFlowOutput> netVirtFlowMap) { this.netVirtFlowMap = netVirtFlowMap; } public HashMap<String, String> getNetVirtAddressSpaceMap() { return netVirtAddressSpaceMap; } public void setNetVirtAddressSpaceMap( HashMap<String, String> netVirtAddressSpaceMap) { this.netVirtAddressSpaceMap = netVirtAddressSpaceMap; } } /** * Example of the outout * { * "netVirtCount": 1, * "netVirtAddressSpaceMap" : { "default": "default", }, * "netVirtFlowMap": { * "default": { * "flowCount": 20, * "flowList": [ * { * "dpid": "00:00:00:00:00:00:00:0f", * "flowEntry": { * "actions": [ * { * "length": 8, * "lengthU": 8, * "maxLength": 0, * "port": 2, * "type": "OUTPUT" * } * ], * "byteCount": 6664, * "cookie": 9007199254740992, * "durationNanoseconds": 162000000, * "durationSeconds": 67, * "hardTimeout": 0, * "idleTimeout": 5, * "length": 96, * "match": { * "dataLayerDestination": "00:00:00:00:00:08", * "dataLayerSource": "00:00:00:00:00:01", * "dataLayerType": 2048, * "dataLayerVirtualLan": -1, * "dataLayerVirtualLanPriorityCodePoint": 0, * "inputPort": 3, * "networkDestination": "0.0.0.0", * "networkDestinationMaskLen": 0, * "networkProtocol": 0, * "networkSource": "0.0.0.0", * "networkSourceMaskLen": 0, * "networkTypeOfService": 0, * "transportDestination": 0, * "transportSource": 0, * "wildcards": 4194272 * }, * "packetCount": 68, * "priority": 0, * "tableId": 1 * } * }, ... * */ @Get("json") @LogMessageDoc(level="ERROR", message="findDevice() called without switch/port information.", explanation="Invalid REST API request", recommendation=LogMessageDoc.GENERIC_ACTION) public MultiNetVirtFlowOutput handleNetVirtFlow() { MultiNetVirtFlowOutput output = new MultiNetVirtFlowOutput(); IDeviceService deviceManager = (IDeviceService)getContext().getAttributes(). get(IDeviceService.class.getCanonicalName()); INetVirtManagerService netVirtManager = (INetVirtManagerService)getContext().getAttributes(). get(INetVirtManagerService.class.getCanonicalName()); // Get the NetVirt Name String paramNetVirtName = (String)getRequestAttributes().get("netVirtName"); if (paramNetVirtName == null) { paramNetVirtName = "all"; } if (paramNetVirtName.contains("%7C")) { paramNetVirtName=paramNetVirtName.replace("%7C", "|"); } if ((!paramNetVirtName.contains("|")) && (!paramNetVirtName.equals("all"))) { paramNetVirtName="default|".concat(paramNetVirtName); } // Get all the flows from all the switches Map<String, Object> allSwitchFlows = retrieveInternal("flow"); // Extract the src-mac, dst-mac and vlan from the flows and then // find out which netVirt the flow belongs for (String switchIdString : allSwitchFlows.keySet()) { @SuppressWarnings("unchecked") List<OFFlowStatisticsReply> oneSwitchStatsList = (List<OFFlowStatisticsReply>)(allSwitchFlows.get(switchIdString)); Long switchId = HexString.toLong(switchIdString); for (OFFlowStatisticsReply oneStatsEntry : oneSwitchStatsList) { OFMatch match = oneStatsEntry.getMatch(); long srcMac = Ethernet.toLong(match.getDataLayerSource()); long dstMac = Ethernet.toLong(match.getDataLayerDestination()); // Find the NetVirt name for the flow IDevice src = null; IDevice dst = null; try { src = deviceManager.findDevice(srcMac, match.getDataLayerVirtualLan(), match.getNetworkSource(), switchId, Integer.valueOf(match.getInputPort())); if (src != null) { dst = deviceManager.findClassDevice(src.getEntityClass(), dstMac, match.getDataLayerVirtualLan(), match.getNetworkDestination()); } } catch (IllegalArgumentException e) { log.error("findDevice() called without switch/port information."); continue; } // Retrieve interfaces for source and destination List<VNSInterface> srcIfaces = null; List<VNSInterface> dstIfaces = null; if (src != null) { // Retrieve interfaces for source and destination srcIfaces = netVirtManager.getInterfaces(src); } if (dst != null) { // Retrieve interfaces for source and destination dstIfaces = netVirtManager.getInterfaces(dst); } if (srcIfaces == null || dstIfaces == null) continue; VNS netVirtChosen=null; // Find the matching NetVirt with the highest priority for (VNSInterface sface : srcIfaces) { for (VNSInterface dface : dstIfaces) { if (sface.getParentVNS() == dface.getParentVNS()) { if (netVirtChosen == null || sface.getParentVNS().compareTo(netVirtChosen) < 0) { netVirtChosen = sface.getParentVNS(); } break; // NetVirt can't repeat in dstIfaces } } } if (netVirtChosen == null) { // Skip if src and dst devices is not in any common netVirt. continue; } if ((!paramNetVirtName.equalsIgnoreCase("all")) && (!paramNetVirtName.equals(netVirtChosen.getName()))) { // Skip this flow continue; } // Found the NetVirt, now add it to the output structure. Don't sort the output here // as it has to be sorted anyways in the cli handler by aliases etc. String netVirtName = netVirtChosen.getName(); // Get the flow map for the netVirt if any PerNetVirtFlowOutput perNetVirtfout = output.netVirtFlowMap.get(netVirtName); if (perNetVirtfout == null) { // First time seeing this netVirt output.netVirtCount++; // Add this netVirt to the hashmap perNetVirtfout = new PerNetVirtFlowOutput(); perNetVirtfout.flowList = new ArrayList<OFFlowStatsReplywithDpid>(); OFFlowStatsReplywithDpid statsEntry = new OFFlowStatsReplywithDpid(); statsEntry.dpid = switchIdString; statsEntry.flowEntry = oneStatsEntry; perNetVirtfout.flowList.add(statsEntry); output.netVirtFlowMap.put(netVirtName, perNetVirtfout); output.netVirtAddressSpaceMap.put( netVirtName, netVirtChosen.getAddressSpaceName()); } else { // netVirt already exists in the output object, Just add this flow to the list perNetVirtfout.flowCount++; OFFlowStatsReplywithDpid statsEntry = new OFFlowStatsReplywithDpid(); statsEntry.dpid = switchIdString; statsEntry.flowEntry = oneStatsEntry; perNetVirtfout.flowList.add(statsEntry); } } } // Return the output , jackson and restlet infra will convert output to json object // and would return it to the REST API caller return output; } }