blob: 26d1302a354d89bb2392ed5eed0e69efc55e1254 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
17
18/**
19 * Implements a very simple central store for system counters
20 */
21package net.floodlightcontroller.counter;
22
23import java.util.ArrayList;
24import java.util.Collection;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.List;
28import java.util.Map;
29import java.util.concurrent.ConcurrentHashMap;
30import java.util.concurrent.Executors;
31import java.util.concurrent.TimeUnit;
32
33import javax.annotation.PostConstruct;
34
35import net.floodlightcontroller.core.IOFSwitch;
36import net.floodlightcontroller.core.module.FloodlightModuleContext;
37import net.floodlightcontroller.core.module.FloodlightModuleException;
38import net.floodlightcontroller.core.module.IFloodlightModule;
39import net.floodlightcontroller.core.module.IFloodlightService;
40import net.floodlightcontroller.counter.CounterValue.CounterType;
41import net.floodlightcontroller.packet.Ethernet;
42import net.floodlightcontroller.packet.IPv4;
43
44import org.openflow.protocol.OFMessage;
45import org.openflow.protocol.OFPacketIn;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
49
50/**
51 * @author kyle
52 *
53 */
54public class CounterStore implements IFloodlightModule, ICounterStoreService {
55 protected static Logger log = LoggerFactory.getLogger(CounterStore.class);
56
57 public enum NetworkLayer {
58 L2, L3, L4
59 }
60
61 protected class CounterEntry {
62 protected ICounter counter;
63 String title;
64 }
65
66 /**
67 * A map of counterName --> Counter
68 */
69 protected ConcurrentHashMap<String, CounterEntry> nameToCEIndex =
70 new ConcurrentHashMap<String, CounterEntry>();
71
72 protected ICounter heartbeatCounter;
73 protected ICounter randomCounter;
74
75 /**
76 * Counter Categories grouped by network layers
77 * NetworkLayer -> CounterToCategories
78 */
79 protected static Map<NetworkLayer, Map<String, List<String>>> layeredCategories =
80 new ConcurrentHashMap<NetworkLayer, Map<String, List<String>>> ();
81
82 public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) {
83 OFPacketIn packet = (OFPacketIn)m;
84
85 // Make sure there is data
86 if (packet.getPacketData().length <= 0) return;
87
88 /* Extract the etherType and protocol field for IPv4 packet.
89 */
90 String etherType = String.format("%04x", eth.getEtherType());
91
92 /*
93 * Valid EtherType must be greater than or equal to 0x0600
94 * It is V1 Ethernet Frame if EtherType < 0x0600
95 */
96 if (eth.getEtherType() < 0x0600) {
97 etherType = "0599";
98 }
99
100 if (TypeAliases.l3TypeAliasMap != null &&
101 TypeAliases.l3TypeAliasMap.containsKey(etherType)) {
102 etherType = TypeAliases.l3TypeAliasMap.get(etherType);
103 } else {
104 etherType = "L3_" + etherType;
105 }
106 String switchIdHex = sw.getStringId();
107
108 String packetName = m.getType().toClass().getName();
109 packetName = packetName.substring(packetName.lastIndexOf('.')+1);
110
111 // Construct controller counter for the packet_in
112 String controllerCounterName =
113 CounterStore.createCounterName(CONTROLLER_NAME,
114 -1,
115 packetName);
116
117 String controllerL3CategoryCounterName =
118 CounterStore.createCounterName(CONTROLLER_NAME,
119 -1,
120 packetName,
121 etherType,
122 NetworkLayer.L3);
123
124 String l2Type = null;
125 if (eth.isBroadcast()) {
126 l2Type = BROADCAST;
127 } else if (eth.isMulticast()) {
128 l2Type = MULTICAST;
129 } else {
130 l2Type = UNICAST;
131 }
132
133 // Construct both port and switch L3 counter for the packet_in
134 String controllerL2CategoryCounterName = CounterStore.createCounterName(CONTROLLER_NAME,
135 -1,
136 packetName,
137 l2Type,
138 NetworkLayer.L2);
139 String switchL2CategoryCounterName = CounterStore.createCounterName(switchIdHex,
140 -1,
141 packetName,
142 l2Type,
143 NetworkLayer.L2);
144 String portL2CategoryCounterName = CounterStore.createCounterName(switchIdHex,
145 packet.getInPort(),
146 packetName,
147 l2Type,
148 NetworkLayer.L2);
149
150 // Construct both port and switch L3 counter for the packet_in
151 String portCounterName =
152 CounterStore.createCounterName(switchIdHex,
153 packet.getInPort(),
154 packetName);
155 String switchCounterName =
156 CounterStore.createCounterName(switchIdHex,
157 -1,
158 packetName);
159
160 String portL3CategoryCounterName =
161 CounterStore.createCounterName(switchIdHex,
162 packet.getInPort(),
163 packetName,
164 etherType,
165 NetworkLayer.L3);
166 String switchL3CategoryCounterName =
167 CounterStore.createCounterName(switchIdHex,
168 -1,
169 packetName,
170 etherType,
171 NetworkLayer.L3);
172
173 // Controller counters
174 ICounter controllerCounter = getCounter(controllerCounterName);
175 if (controllerCounter == null) {
176 controllerCounter = createCounter(controllerCounterName,
177 CounterType.LONG);
178 }
179 controllerCounter.increment();
180 ICounter portCounter = getCounter(portCounterName);
181 if (portCounter == null) {
182 portCounter = createCounter(portCounterName,
183 CounterType.LONG);
184 }
185 portCounter.increment();
186 ICounter switchCounter = getCounter(switchCounterName);
187 if (switchCounter == null) {
188 switchCounter = createCounter(switchCounterName,
189 CounterType.LONG);
190 }
191 switchCounter.increment();
192
193 // L2 counters
194 ICounter controllerL2Counter = getCounter(controllerL2CategoryCounterName);
195 if (controllerL2Counter == null) {
196 controllerL2Counter = createCounter(controllerL2CategoryCounterName,
197 CounterType.LONG);
198 }
199 controllerL2Counter.increment();
200 ICounter switchL2Counter = getCounter(switchL2CategoryCounterName);
201 if (switchL2Counter == null) {
202 switchL2Counter = createCounter(switchL2CategoryCounterName,
203 CounterType.LONG);
204 }
205 switchL2Counter.increment();
206 ICounter portL2Counter = getCounter(portL2CategoryCounterName);
207 if (portL2Counter == null) {
208 portL2Counter = createCounter(portL2CategoryCounterName,
209 CounterType.LONG);
210 }
211 portL2Counter.increment();
212
213 // L3 counters
214 ICounter controllerL3Counter = getCounter(controllerL3CategoryCounterName);
215 if (controllerL3Counter == null) {
216 controllerL3Counter = createCounter(controllerL3CategoryCounterName,
217 CounterType.LONG);
218 }
219 controllerL3Counter.increment();
220 ICounter portL3Counter = getCounter(portL3CategoryCounterName);
221 if (portL3Counter == null) {
222 portL3Counter = createCounter(portL3CategoryCounterName,
223 CounterType.LONG);
224 }
225 portL3Counter.increment();
226 ICounter switchL3Counter = getCounter(switchL3CategoryCounterName);
227 if (switchL3Counter == null) {
228 switchL3Counter = createCounter(switchL3CategoryCounterName,
229 CounterType.LONG);
230 }
231 switchL3Counter.increment();
232
233 // L4 counters
234 if (etherType.compareTo(CounterStore.L3ET_IPV4) == 0) {
235 IPv4 ipV4 = (IPv4)eth.getPayload();
236 String l4Type = String.format("%02x", ipV4.getProtocol());
237 if (TypeAliases.l4TypeAliasMap != null &&
238 TypeAliases.l4TypeAliasMap.containsKey(l4Type)) {
239 l4Type = TypeAliases.l4TypeAliasMap.get(l4Type);
240 } else {
241 l4Type = "L4_" + l4Type;
242 }
243 String controllerL4CategoryCounterName =
244 CounterStore.createCounterName(CONTROLLER_NAME,
245 -1,
246 packetName,
247 l4Type,
248 NetworkLayer.L4);
249 String portL4CategoryCounterName =
250 CounterStore.createCounterName(switchIdHex,
251 packet.getInPort(),
252 packetName,
253 l4Type,
254 NetworkLayer.L4);
255 String switchL4CategoryCounterName =
256 CounterStore.createCounterName(switchIdHex,
257 -1,
258 packetName,
259 l4Type,
260 NetworkLayer.L4);
261 ICounter controllerL4Counter = getCounter(controllerL4CategoryCounterName);
262 if (controllerL4Counter == null) {
263 controllerL4Counter = createCounter(controllerL4CategoryCounterName,
264 CounterType.LONG);
265 }
266 controllerL4Counter.increment();
267 ICounter portL4Counter = getCounter(portL4CategoryCounterName);
268 if (portL4Counter == null) {
269 portL4Counter = createCounter(portL4CategoryCounterName,
270 CounterType.LONG);
271 }
272 portL4Counter.increment();
273 ICounter switchL4Counter = getCounter(switchL4CategoryCounterName);
274 if (switchL4Counter == null) {
275 switchL4Counter = createCounter(switchL4CategoryCounterName,
276 CounterType.LONG);
277 }
278 switchL4Counter.increment();
279 }
280 }
281
282 /**
283 * This method can only be used to update packetOut and flowmod counters
284 *
285 * @param sw
286 * @param ofMsg
287 */
288 public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg) {
289 String packetName = ofMsg.getType().toClass().getName();
290 packetName = packetName.substring(packetName.lastIndexOf('.')+1);
291 // flowmod is per switch and controller. portid = -1
292 String controllerFMCounterName = CounterStore.createCounterName(CONTROLLER_NAME, -1, packetName);
293 ICounter counter = getCounter(controllerFMCounterName);
294 if (counter == null) {
295 counter = createCounter(controllerFMCounterName, CounterValue.CounterType.LONG);
296 }
297 counter.increment();
298
299 String switchFMCounterName = CounterStore.createCounterName(sw.getStringId(), -1, packetName);
300 counter = getCounter(switchFMCounterName);
301 if (counter == null) {
302 counter = createCounter(switchFMCounterName, CounterValue.CounterType.LONG);
303 }
304 counter.increment();
305 }
306
307
308 /**
309 * Create a title based on switch ID, portID, vlanID, and counterName
310 * If portID is -1, the title represents the given switch only
311 * If portID is a non-negative number, the title represents the port on the given switch
312 */
313 public static String createCounterName(String switchID, int portID, String counterName) {
314 if (portID < 0) {
315 return switchID + TitleDelimitor + counterName;
316 } else {
317 return switchID + TitleDelimitor + portID + TitleDelimitor + counterName;
318 }
319 }
320
321 /**
322 * Create a title based on switch ID, portID, vlanID, counterName, and subCategory
323 * If portID is -1, the title represents the given switch only
324 * If portID is a non-negative number, the title represents the port on the given switch
325 * For example: PacketIns can be further categorized based on L2 etherType or L3 protocol
326 */
327 public static String createCounterName(String switchID, int portID, String counterName,
328 String subCategory, NetworkLayer layer) {
329 String fullCounterName = "";
330 String groupCounterName = "";
331
332 if (portID < 0) {
333 groupCounterName = switchID + TitleDelimitor + counterName;
334 fullCounterName = groupCounterName + TitleDelimitor + subCategory;
335 } else {
336 groupCounterName = switchID + TitleDelimitor + portID + TitleDelimitor + counterName;
337 fullCounterName = groupCounterName + TitleDelimitor + subCategory;
338 }
339
340 Map<String, List<String>> counterToCategories;
341 if (layeredCategories.containsKey(layer)) {
342 counterToCategories = layeredCategories.get(layer);
343 } else {
344 counterToCategories = new ConcurrentHashMap<String, List<String>> ();
345 layeredCategories.put(layer, counterToCategories);
346 }
347
348 List<String> categories;
349 if (counterToCategories.containsKey(groupCounterName)) {
350 categories = counterToCategories.get(groupCounterName);
351 } else {
352 categories = new ArrayList<String>();
353 counterToCategories.put(groupCounterName, categories);
354 }
355
356 if (!categories.contains(subCategory)) {
357 categories.add(subCategory);
358 }
359 return fullCounterName;
360 }
361
362 @Override
363 public List<String> getAllCategories(String counterName, NetworkLayer layer) {
364 if (layeredCategories.containsKey(layer)) {
365 Map<String, List<String>> counterToCategories = layeredCategories.get(layer);
366 if (counterToCategories.containsKey(counterName)) {
367 return counterToCategories.get(counterName);
368 }
369 }
370 return null;
371 }
372
373 @Override
374 public ICounter createCounter(String key, CounterValue.CounterType type) {
375 CounterEntry ce;
376 ICounter c;
377
378 c = SimpleCounter.createCounter(new Date(), type);
379 ce = new CounterEntry();
380 ce.counter = c;
381 ce.title = key;
382 nameToCEIndex.putIfAbsent(key, ce);
383
384 return nameToCEIndex.get(key).counter;
385 }
386
387 /**
388 * Post construction init method to kick off the health check and random (test) counter threads
389 */
390 @PostConstruct
391 public void startUp() {
392 this.heartbeatCounter = this.createCounter("CounterStore heartbeat", CounterValue.CounterType.LONG);
393 this.randomCounter = this.createCounter("CounterStore random", CounterValue.CounterType.LONG);
394 //Set a background thread to flush any liveCounters every 100 milliseconds
395 Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
396 public void run() {
397 heartbeatCounter.increment();
398 randomCounter.increment(new Date(), (long) (Math.random() * 100)); //TODO - pull this in to random timing
399 }}, 100, 100, TimeUnit.MILLISECONDS);
400 }
401
402 @Override
403 public ICounter getCounter(String key) {
404 CounterEntry counter = nameToCEIndex.get(key);
405 if (counter != null) {
406 return counter.counter;
407 } else {
408 return null;
409 }
410 }
411
412 /* (non-Javadoc)
413 * @see net.floodlightcontroller.counter.ICounterStoreService#getAll()
414 */
415 @Override
416 public Map<String, ICounter> getAll() {
417 Map<String, ICounter> ret = new ConcurrentHashMap<String, ICounter>();
418 for(Map.Entry<String, CounterEntry> counterEntry : this.nameToCEIndex.entrySet()) {
419 String key = counterEntry.getKey();
420 ICounter counter = counterEntry.getValue().counter;
421 ret.put(key, counter);
422 }
423 return ret;
424 }
425
426 @Override
427 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
428 Collection<Class<? extends IFloodlightService>> services =
429 new ArrayList<Class<? extends IFloodlightService>>(1);
430 services.add(ICounterStoreService.class);
431 return services;
432 }
433
434 @Override
435 public Map<Class<? extends IFloodlightService>, IFloodlightService>
436 getServiceImpls() {
437 Map<Class<? extends IFloodlightService>,
438 IFloodlightService> m =
439 new HashMap<Class<? extends IFloodlightService>,
440 IFloodlightService>();
441 m.put(ICounterStoreService.class, this);
442 return m;
443 }
444
445 @Override
446 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
447 // no-op, no dependencies
448 return null;
449 }
450
451 @Override
452 public void init(FloodlightModuleContext context)
453 throws FloodlightModuleException {
454 // no-op for now
455 }
456
457 @Override
458 public void startUp(FloodlightModuleContext context) {
459 // no-op for now
460 }
461}