/* * 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.HashMap; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.openflow.util.HexString; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.sdnplatform.core.IControllerService; import org.sdnplatform.core.IOFSwitch; import org.sdnplatform.forwarding.Forwarding; import org.sdnplatform.forwarding.IForwardingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class to provide visibility to internal in-memory data of various components * for debugging purposes. * * URI must be in one of the following forms: " + * "http://<controller-hostname>:8080/wm/netVirt/internal-debugs/ * forwarding/<query>/json * * where <query> must be one of (no quotes) * all * switch=<dpid> * switch=all * switch-port=all * switch-port=<dpid>-<port> * * The information can be retrieved using rest API or CLI * * */ public class InternalDebugsForwardingResource extends InternalDebugsResource { private static final String COMPONENT_NAME = "Forwarding"; protected static Logger logger = LoggerFactory.getLogger(InternalDebugsForwardingResource.class); // This is the output structure of the JSON return object public class InternalDebugsFwdOutput extends InternalDebugsOutput { // Broadcast cache hit count for each switch port public Map<Long, Map<Short, Long>> broadcacheHitMapDebugs; public int numberOfTruncatedPacketsSeenDebugs; public boolean broadcastCacheFeatureDebugs; public InternalDebugsFwdOutput(String compName) { super(compName); broadcacheHitMapDebugs = new HashMap<Long, Map<Short, Long>>(); numberOfTruncatedPacketsSeenDebugs = 0; broadcastCacheFeatureDebugs = true; } } public static class ForwardFlagParameters { private Integer broadcastCache; public int getBroadcastCache() { return this.broadcastCache; } public void setBroadcastCache(Integer broadcastCache) { this.broadcastCache = broadcastCache; } @Override public String toString() { return "BroadcastCache = " + this.broadcastCache; } } /** * Handle POST requests: set broadcastCacheFeature flag */ @Post("json") public Map<String,Object> flags (ForwardFlagParameters input) throws Exception{ HashMap<String, Object> model = new HashMap<String,Object>(); if (input == null) { model.put("output", "Invalid output"); return model; } IForwardingService fwdService = (IForwardingService)getContext().getAttributes(). get(IForwardingService.class.getCanonicalName()); fwdService.setBroadcastCache(input.broadcastCache==0 ? false : true); model.put("output", "OK"); return model; } public enum Option { ALL, ALL_SWITCHES, ONE_SWITCH, ALL_SW_PORTS, ONE_SW_PORT, ERROR_BAD_DPID, ERROR_BAD_DPIDERROR, ERROR_BAD_SW_PORT, ERROR } public Option getChoice(String [] params) { final Pattern dpidPattern = Pattern.compile("([A-Fa-f\\d]{2}:?){7}[A-Fa-f\\d]{2}"); final Pattern swPortPattern = Pattern.compile( "([A-Fa-f\\d]{2}:?){7}[A-Fa-f\\d]{2}-[0-9]+"); Matcher m; Option choice = Option.ERROR; if (params.length == 1) { if (params[0].equals("all")) { choice = Option.ALL; } } if (params.length == 2) { if (params[0].equals("switch")) { if (params[1].equals("all")) { choice = Option.ALL_SWITCHES; } else { // check for valid dpid m = dpidPattern.matcher(params[1]); if (m.matches()) { choice = Option.ONE_SWITCH; } else { choice = Option.ERROR_BAD_DPID; } } } if (params[0].equals("switch-port")) { if (params[1].equals("all")) { choice = Option.ALL_SW_PORTS; } else { // check for valid switch-port // expected as, for example (port=45) // switch-port=11:22:33:44:55:66:77:88-45 m = swPortPattern.matcher(params[1]); if (m.matches()) { choice = Option.ONE_SW_PORT; } else { choice = Option.ERROR_BAD_SW_PORT; } } } } // If params length is > 2 then choice would be Option.ERROR as // per initialization of choice return choice; } public void getAllSwitchDebugs(InternalDebugsFwdOutput output, Map<Long, IOFSwitch> switches) { Set<Long> swSet = null; swSet = switches.keySet(); for (Long swIdx : swSet) { IOFSwitch sw = switches.get(swIdx); if (sw != null) { output.broadcacheHitMapDebugs.put(swIdx, sw.getPortBroadcastHits()); } } } @Get("json") public InternalDebugsFwdOutput handleInternalFwdDebugsRequest() { final String BAD_PARAM = "Incorrect URI: URI must be in one of the following forms: " + "http://<controller-hostname>:8080/wm/netVirt/internal-debugs/" + "forwarding/<query>/json where <query> must be all or " + "switch=all or switch=<dpid> or switch-port=<dpid>-<port>"; final String BAD_DPID ="Malformed switch DPID"; final String BAD_SW_PORT = "Malformed switch-port"; final String STATUS_ERROR ="Error"; Long dpid = null; IOFSwitch sw = null; InternalDebugsFwdOutput output = new InternalDebugsFwdOutput(COMPONENT_NAME); // Get the Device dataLayerAddress String param = (String)getRequestAttributes().get("param"); if (param == null) { param = "all"; } String [] params = param.split("="); Option choice = getChoice(params); if (logger.isDebugEnabled()) { logger.debug("Received request for Device Mgrs internal debugs"+ "Param size={}", params.length); for (int idx=0; idx < params.length; idx++) { logger.debug("param[{}]={}", idx, params[idx]); } logger.debug("Choice = {}", choice); } IControllerService flService = (IControllerService)getContext().getAttributes(). get(IControllerService.class.getCanonicalName()); IForwardingService fwdService = (IForwardingService)getContext().getAttributes(). get(IForwardingService.class.getCanonicalName()); Map<Long, IOFSwitch> switches = flService.getSwitches(); boolean found = false; switch (choice) { case ALL: case ALL_SWITCHES: output.broadcastCacheFeatureDebugs = fwdService.getBroadcastCache(); output.numberOfTruncatedPacketsSeenDebugs = ((Forwarding)fwdService).getNumberOfTruncatedPacketsSeen(); getAllSwitchDebugs(output, switches); break; case ONE_SWITCH: dpid = HexString.toLong(params[1]); for (Long swIdx : switches.keySet()) { if (swIdx.equals(dpid)) { // found sw = switches.get(swIdx); found = true; break; } } if (found) { output.broadcacheHitMapDebugs.put(sw.getId(), sw.getPortBroadcastHits());; } else { output.status = STATUS_ERROR; output.reason = "Switch not found"; } break; case ONE_SW_PORT: String [] swport = params[1].split("-"); logger.debug("swport {} {}", swport[0], swport[1]); Short port = Short.parseShort(swport[1]); for (Long swIdx : switches.keySet()) { sw = switches.get(swIdx); if (sw.getStringId().equals(swport[0])) { Map<Short, Long> portBroadcastHits = sw.getPortBroadcastHits(); if (portBroadcastHits.containsKey(port)) { Map<Short, Long> onePortHit = new HashMap<Short, Long>(); onePortHit.put(port, portBroadcastHits.get(port)); output.broadcacheHitMapDebugs.put(swIdx, onePortHit); found = true; } else { output.broadcacheHitMapDebugs.put(swIdx, null); found = false; } break; } } if (!found) { output.status = STATUS_ERROR; output.reason = "Switch-Port not found"; } break; case ERROR_BAD_DPID: output.status = STATUS_ERROR; output.reason = BAD_DPID; break; case ERROR_BAD_SW_PORT: output.status = STATUS_ERROR; output.reason = BAD_SW_PORT; break; default: output.status = STATUS_ERROR; output.reason = BAD_PARAM; } return output; } }