blob: 15ce53b03ad3be949c778293e7a9a4ea103eeb0e [file] [log] [blame]
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
**/
/**
* Implements a very simple central store for system counters
*/
package net.floodlightcontroller.counter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.counter.CounterValue.CounterType;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author kyle
*
*/
public class CounterStore implements IFloodlightModule, ICounterStoreService {
protected final static Logger log = LoggerFactory.getLogger(CounterStore.class);
public enum NetworkLayer {
L2, L3, L4
}
protected class CounterEntry {
protected ICounter counter;
String title;
}
/**
* A map of counterName --> Counter
*/
protected ConcurrentHashMap<String, CounterEntry> nameToCEIndex =
new ConcurrentHashMap<String, CounterEntry>();
protected ICounter heartbeatCounter;
protected ICounter randomCounter;
/**
* Counter Categories grouped by network layers
* NetworkLayer -> CounterToCategories
*/
protected static Map<NetworkLayer, Map<String, List<String>>> layeredCategories =
new ConcurrentHashMap<NetworkLayer, Map<String, List<String>>> ();
public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) {
OFPacketIn packet = (OFPacketIn)m;
// Make sure there is data
if (packet.getPacketData().length <= 0) return;
/* Extract the etherType and protocol field for IPv4 packet.
*/
String etherType = String.format("%04x", eth.getEtherType());
/*
* Valid EtherType must be greater than or equal to 0x0600
* It is V1 Ethernet Frame if EtherType < 0x0600
*/
if (eth.getEtherType() < 0x0600) {
etherType = "0599";
}
if (TypeAliases.l3TypeAliasMap != null &&
TypeAliases.l3TypeAliasMap.containsKey(etherType)) {
etherType = TypeAliases.l3TypeAliasMap.get(etherType);
} else {
etherType = "L3_" + etherType;
}
String switchIdHex = sw.getStringId();
String packetName = m.getType().toClass().getName();
packetName = packetName.substring(packetName.lastIndexOf('.')+1);
// Construct controller counter for the packet_in
String controllerCounterName =
CounterStore.createCounterName(CONTROLLER_NAME,
-1,
packetName);
String controllerL3CategoryCounterName =
CounterStore.createCounterName(CONTROLLER_NAME,
-1,
packetName,
etherType,
NetworkLayer.L3);
String l2Type = null;
if (eth.isBroadcast()) {
l2Type = BROADCAST;
} else if (eth.isMulticast()) {
l2Type = MULTICAST;
} else {
l2Type = UNICAST;
}
// Construct both port and switch L3 counter for the packet_in
String controllerL2CategoryCounterName = CounterStore.createCounterName(CONTROLLER_NAME,
-1,
packetName,
l2Type,
NetworkLayer.L2);
String switchL2CategoryCounterName = CounterStore.createCounterName(switchIdHex,
-1,
packetName,
l2Type,
NetworkLayer.L2);
String portL2CategoryCounterName = CounterStore.createCounterName(switchIdHex,
packet.getInPort(),
packetName,
l2Type,
NetworkLayer.L2);
// Construct both port and switch L3 counter for the packet_in
String portCounterName =
CounterStore.createCounterName(switchIdHex,
packet.getInPort(),
packetName);
String switchCounterName =
CounterStore.createCounterName(switchIdHex,
-1,
packetName);
String portL3CategoryCounterName =
CounterStore.createCounterName(switchIdHex,
packet.getInPort(),
packetName,
etherType,
NetworkLayer.L3);
String switchL3CategoryCounterName =
CounterStore.createCounterName(switchIdHex,
-1,
packetName,
etherType,
NetworkLayer.L3);
// Controller counters
ICounter controllerCounter = getCounter(controllerCounterName);
if (controllerCounter == null) {
controllerCounter = createCounter(controllerCounterName,
CounterType.LONG);
}
controllerCounter.increment();
ICounter portCounter = getCounter(portCounterName);
if (portCounter == null) {
portCounter = createCounter(portCounterName,
CounterType.LONG);
}
portCounter.increment();
ICounter switchCounter = getCounter(switchCounterName);
if (switchCounter == null) {
switchCounter = createCounter(switchCounterName,
CounterType.LONG);
}
switchCounter.increment();
// L2 counters
ICounter controllerL2Counter = getCounter(controllerL2CategoryCounterName);
if (controllerL2Counter == null) {
controllerL2Counter = createCounter(controllerL2CategoryCounterName,
CounterType.LONG);
}
controllerL2Counter.increment();
ICounter switchL2Counter = getCounter(switchL2CategoryCounterName);
if (switchL2Counter == null) {
switchL2Counter = createCounter(switchL2CategoryCounterName,
CounterType.LONG);
}
switchL2Counter.increment();
ICounter portL2Counter = getCounter(portL2CategoryCounterName);
if (portL2Counter == null) {
portL2Counter = createCounter(portL2CategoryCounterName,
CounterType.LONG);
}
portL2Counter.increment();
// L3 counters
ICounter controllerL3Counter = getCounter(controllerL3CategoryCounterName);
if (controllerL3Counter == null) {
controllerL3Counter = createCounter(controllerL3CategoryCounterName,
CounterType.LONG);
}
controllerL3Counter.increment();
ICounter portL3Counter = getCounter(portL3CategoryCounterName);
if (portL3Counter == null) {
portL3Counter = createCounter(portL3CategoryCounterName,
CounterType.LONG);
}
portL3Counter.increment();
ICounter switchL3Counter = getCounter(switchL3CategoryCounterName);
if (switchL3Counter == null) {
switchL3Counter = createCounter(switchL3CategoryCounterName,
CounterType.LONG);
}
switchL3Counter.increment();
// L4 counters
if (etherType.compareTo(CounterStore.L3ET_IPV4) == 0) {
IPv4 ipV4 = (IPv4)eth.getPayload();
String l4Type = String.format("%02x", ipV4.getProtocol());
if (TypeAliases.l4TypeAliasMap != null &&
TypeAliases.l4TypeAliasMap.containsKey(l4Type)) {
l4Type = TypeAliases.l4TypeAliasMap.get(l4Type);
} else {
l4Type = "L4_" + l4Type;
}
String controllerL4CategoryCounterName =
CounterStore.createCounterName(CONTROLLER_NAME,
-1,
packetName,
l4Type,
NetworkLayer.L4);
String portL4CategoryCounterName =
CounterStore.createCounterName(switchIdHex,
packet.getInPort(),
packetName,
l4Type,
NetworkLayer.L4);
String switchL4CategoryCounterName =
CounterStore.createCounterName(switchIdHex,
-1,
packetName,
l4Type,
NetworkLayer.L4);
ICounter controllerL4Counter = getCounter(controllerL4CategoryCounterName);
if (controllerL4Counter == null) {
controllerL4Counter = createCounter(controllerL4CategoryCounterName,
CounterType.LONG);
}
controllerL4Counter.increment();
ICounter portL4Counter = getCounter(portL4CategoryCounterName);
if (portL4Counter == null) {
portL4Counter = createCounter(portL4CategoryCounterName,
CounterType.LONG);
}
portL4Counter.increment();
ICounter switchL4Counter = getCounter(switchL4CategoryCounterName);
if (switchL4Counter == null) {
switchL4Counter = createCounter(switchL4CategoryCounterName,
CounterType.LONG);
}
switchL4Counter.increment();
}
}
/**
* This method can only be used to update packetOut and flowmod counters
*
* @param sw
* @param ofMsg
*/
public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg) {
String packetName = ofMsg.getType().toClass().getName();
packetName = packetName.substring(packetName.lastIndexOf('.')+1);
// flowmod is per switch and controller. portid = -1
String controllerFMCounterName = CounterStore.createCounterName(CONTROLLER_NAME, -1, packetName);
ICounter counter = getCounter(controllerFMCounterName);
if (counter == null) {
counter = createCounter(controllerFMCounterName, CounterValue.CounterType.LONG);
}
counter.increment();
String switchFMCounterName = CounterStore.createCounterName(sw.getStringId(), -1, packetName);
counter = getCounter(switchFMCounterName);
if (counter == null) {
counter = createCounter(switchFMCounterName, CounterValue.CounterType.LONG);
}
counter.increment();
}
/**
* Create a title based on switch ID, portID, vlanID, and counterName
* If portID is -1, the title represents the given switch only
* If portID is a non-negative number, the title represents the port on the given switch
*/
public static String createCounterName(String switchID, int portID, String counterName) {
if (portID < 0) {
return switchID + TitleDelimitor + counterName;
} else {
return switchID + TitleDelimitor + portID + TitleDelimitor + counterName;
}
}
/**
* Create a title based on switch ID, portID, vlanID, counterName, and subCategory
* If portID is -1, the title represents the given switch only
* If portID is a non-negative number, the title represents the port on the given switch
* For example: PacketIns can be further categorized based on L2 etherType or L3 protocol
*/
public static String createCounterName(String switchID, int portID, String counterName,
String subCategory, NetworkLayer layer) {
String fullCounterName = "";
String groupCounterName = "";
if (portID < 0) {
groupCounterName = switchID + TitleDelimitor + counterName;
fullCounterName = groupCounterName + TitleDelimitor + subCategory;
} else {
groupCounterName = switchID + TitleDelimitor + portID + TitleDelimitor + counterName;
fullCounterName = groupCounterName + TitleDelimitor + subCategory;
}
Map<String, List<String>> counterToCategories;
if (layeredCategories.containsKey(layer)) {
counterToCategories = layeredCategories.get(layer);
} else {
counterToCategories = new ConcurrentHashMap<String, List<String>> ();
layeredCategories.put(layer, counterToCategories);
}
List<String> categories;
if (counterToCategories.containsKey(groupCounterName)) {
categories = counterToCategories.get(groupCounterName);
} else {
categories = new ArrayList<String>();
counterToCategories.put(groupCounterName, categories);
}
if (!categories.contains(subCategory)) {
categories.add(subCategory);
}
return fullCounterName;
}
@Override
public List<String> getAllCategories(String counterName, NetworkLayer layer) {
if (layeredCategories.containsKey(layer)) {
Map<String, List<String>> counterToCategories = layeredCategories.get(layer);
if (counterToCategories.containsKey(counterName)) {
return counterToCategories.get(counterName);
}
}
return null;
}
@Override
public ICounter createCounter(String key, CounterValue.CounterType type) {
CounterEntry ce;
ICounter c;
c = SimpleCounter.createCounter(new Date(), type);
ce = new CounterEntry();
ce.counter = c;
ce.title = key;
nameToCEIndex.putIfAbsent(key, ce);
return nameToCEIndex.get(key).counter;
}
/**
* Post construction init method to kick off the health check and random (test) counter threads
*/
@PostConstruct
public void startUp() {
this.heartbeatCounter = this.createCounter("CounterStore heartbeat", CounterValue.CounterType.LONG);
this.randomCounter = this.createCounter("CounterStore random", CounterValue.CounterType.LONG);
//Set a background thread to flush any liveCounters every 100 milliseconds
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
heartbeatCounter.increment();
randomCounter.increment(new Date(), (long) (Math.random() * 100)); //TODO - pull this in to random timing
}}, 100, 100, TimeUnit.MILLISECONDS);
}
@Override
public ICounter getCounter(String key) {
CounterEntry counter = nameToCEIndex.get(key);
if (counter != null) {
return counter.counter;
} else {
return null;
}
}
/* (non-Javadoc)
* @see net.floodlightcontroller.counter.ICounterStoreService#getAll()
*/
@Override
public Map<String, ICounter> getAll() {
Map<String, ICounter> ret = new ConcurrentHashMap<String, ICounter>();
for(Map.Entry<String, CounterEntry> counterEntry : this.nameToCEIndex.entrySet()) {
String key = counterEntry.getKey();
ICounter counter = counterEntry.getValue().counter;
ret.put(key, counter);
}
return ret;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> services =
new ArrayList<Class<? extends IFloodlightService>>(1);
services.add(ICounterStoreService.class);
return services;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() {
Map<Class<? extends IFloodlightService>,
IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>,
IFloodlightService>();
m.put(ICounterStoreService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
// no-op, no dependencies
return null;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// no-op for now
}
@Override
public void startUp(FloodlightModuleContext context) {
// no-op for now
}
}