blob: 3dd35bc07502d4db5f9b16ebe4da8978379b5497 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.debugcounter.web;
2
3import java.util.HashMap;
4import java.util.List;
5import java.util.Map;
6
7
8import org.restlet.resource.Get;
9import org.restlet.resource.Post;
10import org.slf4j.Logger;
11import org.slf4j.LoggerFactory;
12
13import net.floodlightcontroller.debugcounter.DebugCounter.DebugCounterInfo;
14import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
15
16/**
17 * Web interface for Debug Counters
18 *
19 * @author Saurav
20 */
21public class DebugCounterResource extends DebugCounterResourceBase {
22 protected static Logger logger =
23 LoggerFactory.getLogger(DebugCounterResource.class);
24
25 /**
26 * The output JSON model that contains the counter information
27 */
28 public class DebugCounterInfoOutput {
29 protected class DCInfo {
30 private final Long counterValue;
31 private final CounterType counterType;
32 private final String counterDesc;
33 private final boolean enabled;
34 private final String counterHierarchy;
35 private final String moduleName;
36 private final String[] metaData;
37
38 DCInfo(DebugCounterInfo dci) {
39 this.moduleName = dci.getCounterInfo().getModuleName();
40 this.counterHierarchy = dci.getCounterInfo().getCounterHierarchy();
41 this.counterDesc = dci.getCounterInfo().getCounterDesc();
42 this.metaData = dci.getCounterInfo().getMetaData();
43 this.enabled = dci.getCounterInfo().isEnabled();
44 this.counterType = dci.getCounterInfo().getCtype();
45 this.counterValue = dci.getCounterValue();
46 }
47
48 public Long getCounterValue() {
49 return counterValue;
50 }
51 public CounterType getCounterType() {
52 return counterType;
53 }
54
55 public String getCounterDesc() {
56 return counterDesc;
57 }
58
59 public boolean isEnabled() {
60 return enabled;
61 }
62
63 public String getCounterHierarchy() {
64 return counterHierarchy;
65 }
66
67 public String getModuleName() {
68 return moduleName;
69 }
70
71 public String[] getMetaData() {
72 return metaData;
73 }
74
75 }
76 // complete counter information - null if only names are requested or
77 // if an error occurs
78 public Map<String, DCInfo> counterMap;
79 // list of names could be just moduleNames or counter hierarchical names
80 // for a specific module
81 public List<String> names;
82
83 public String error;
84
85 DebugCounterInfoOutput(boolean getList) {
86 if (!getList) {
87 counterMap = new HashMap<String, DCInfo>();
88 }
89 error = null;
90 }
91 public Map<String, DCInfo> getCounterMap() {
92 return counterMap;
93 }
94
95 public String getError() {
96 return error;
97 }
98
99 public List<String> getNames() {
100 return names;
101 }
102
103 }
104
105 public enum Option {
106 ALL, ONE_MODULE, MODULE_COUNTER_HIERARCHY, ERROR_BAD_MODULE_NAME,
107 ERROR_BAD_PARAM,
108 ERROR_BAD_MODULE_COUNTER_NAME
109 }
110
111 public static class CounterPost {
112 public Boolean reset;
113 public Boolean enable;
114
115 public Boolean getReset() {
116 return reset;
117 }
118 public void setReset(Boolean reset) {
119 this.reset = reset;
120 }
121 public Boolean getEnable() {
122 return enable;
123 }
124 public void setEnable(Boolean enable) {
125 this.enable = enable;
126 }
127 }
128
129 public static class ResetOutput {
130 String error = null;
131
132 public String getError() {
133 return error;
134 }
135 public void setError(String error) {
136 this.error = error;
137 }
138 }
139
140 /**
141 * Reset or enable/disable counters
142 *
143 * If using curl:
144 * curl -X POST -d DATA -H "Content-Type: application/json" URL
145 * where DATA must be one of the following:
146 * {\"reset\":true} to reset counters
147 * {\"enable\":true} to enable counter
148 * {\"enable\":false} to disable counter
149 * and URL must be in one of the following forms:
150 * "http://{controller-hostname}:8080/wm/debugcounter/{param1}/{param2}/{param3}/{param4}
151 *
152 * {param1} can be null, 'all' or the name of a module {moduleName}.
153 * {param2}/{param3}/{param4} refer to hierarchical counter names.
154 *
155 * The Reset command will reset the counter specified as well as all counters
156 * in the hierarchical levels below. For example, if a counter hierarchy exists
157 * as switch/00:00:00:00:01:02:03:04/pktin/drops, then a reset command with just
158 * the moduleName (param1=switch) and counterHierarchy (param2=00:00:00:00:01:02:03:04)
159 * will reset all counters for that switch. Continuing the example -
160 * for a counterHierarchy (param2=00:00:00:00:01:02:03:04 and param3=pktin), the reset
161 * command will remove all pktin counters for that switch.
162 *
163 * The enable/disable command will ONLY disable a specific counter (and only if
164 * that counter is of CounterType.ON_DEMAND)
165 * It will not enable/disable counters at any other hierarchical level.
166 *
167 */
168 @Post
169 public ResetOutput postHandler(CounterPost postData) {
170 ResetOutput output = new ResetOutput();
171 Option choice = Option.ERROR_BAD_PARAM;
172 String param1 = (String)getRequestAttributes().get("param1");
173 String param2 = (String)getRequestAttributes().get("param2");
174 String param3 = (String)getRequestAttributes().get("param3");
175 String param4 = (String)getRequestAttributes().get("param4");
176 String moduleName = "";
177
178 if (param1 == null) {
179 moduleName = "all";
180 choice = Option.ALL;
181 } else if (param1.equals("all")) {
182 moduleName = "all";
183 choice = Option.ALL;
184 } else {
185 moduleName = param1;
186 }
187
188 String counterHierarchy = "";
189 if (param2 != null) {
190 counterHierarchy += param2;
191 if (param3 != null) {
192 counterHierarchy += "/" + param3;
193 if (param4 != null) {
194 counterHierarchy += "/" + param4;
195 }
196 }
197 }
198
199 if (!moduleName.equals("all") && counterHierarchy.equals("")) {
200 // only module name specified
201 boolean isRegistered = debugCounter.containsModuleName(param1);
202 if (isRegistered) {
203 choice = Option.ONE_MODULE;
204 } else {
205 choice = Option.ERROR_BAD_MODULE_NAME;
206 }
207 } else if (!moduleName.equals("all") && !counterHierarchy.equals("")) {
208 // both module and counter names specified
209 boolean isRegistered = debugCounter.
210 containsModuleCounterHierarchy(moduleName, counterHierarchy);
211 if (isRegistered) {
212 choice = Option.MODULE_COUNTER_HIERARCHY;
213 } else {
214 choice = Option.ERROR_BAD_MODULE_COUNTER_NAME;
215 }
216 }
217
218 boolean reset = false;
219 boolean turnOnOff = false;
220 if (postData.getReset() != null && postData.getReset()) {
221 reset = true;
222 }
223 if (postData.getEnable() != null) {
224 turnOnOff = true;
225 }
226
227 switch (choice) {
228 case ALL:
229 if (reset) debugCounter.resetAllCounters();
230 break;
231 case ONE_MODULE:
232 if (reset) debugCounter.resetAllModuleCounters(moduleName);
233 break;
234 case MODULE_COUNTER_HIERARCHY:
235 if (reset)
236 debugCounter.resetCounterHierarchy(moduleName, counterHierarchy);
237 else if (turnOnOff && postData.getEnable())
238 debugCounter.enableCtrOnDemand(moduleName, counterHierarchy);
239 else if (turnOnOff && !postData.getEnable())
240 debugCounter.disableCtrOnDemand(moduleName, counterHierarchy);
241 break;
242 case ERROR_BAD_MODULE_NAME:
243 output.error = "Module name has no corresponding registered counters";
244 break;
245 case ERROR_BAD_MODULE_COUNTER_NAME:
246 output.error = "Counter not registered";
247 break;
248 case ERROR_BAD_PARAM:
249 output.error = "Bad param";
250 }
251
252 return output;
253 }
254
255 /**
256 * Return the debug counter data for the get rest-api call
257 *
258 * URI must be in one of the following forms:
259 * "http://{controller-hostname}:8080/wm/debugcounter/{param1}/{param2}/{param3}/{param4}"
260 *
261 * where {param1} must be one of (no quotes):
262 * null if nothing is given then by default all
263 * module names are returned for which counters
264 * have been registered
265 * "all" returns value/info on all counters.
266 * "{moduleName}" returns value/info on all counters for
267 * the specified module 'moduelName'.
268 * & {param2}/{param3}/{param4} refer to hierarchical counter names.
269 * eg. 00:00:00:00:01:02:03:04/pktin/drops
270 * where leaving out any of the params returns
271 * all counters in the hierarchical level below.
272 * So giving just the switch dpid will fetch
273 * all counters for that switch.
274 * A special case => if param2 is null, then
275 * all hierarchical counterNames are returned
276 * for the given moduleName (in param1)
277 */
278 @Get
279 public DebugCounterInfoOutput handleCounterInfoQuery() {
280 DebugCounterInfoOutput output;
281 Option choice = Option.ERROR_BAD_PARAM;
282 String param1 = (String)getRequestAttributes().get("param1");
283 String param2 = (String)getRequestAttributes().get("param2");
284 String param3 = (String)getRequestAttributes().get("param3");
285 String param4 = (String)getRequestAttributes().get("param4");
286
287 if (param1 == null) {
288 output = new DebugCounterInfoOutput(true);
289 return listCounters(output);
290 } else if (param1.equals("all")) {
291 output = new DebugCounterInfoOutput(false);
292 populateCounters(debugCounter.getAllCounterValues(), output);
293 return output;
294 }
295
296 output = new DebugCounterInfoOutput(false);
297 String counterHierarchy = "";
298 if (param2 == null) {
299 // param2 is null -- return list of counternames for param1
300 boolean isRegistered = debugCounter.containsModuleName(param1);
301 output = new DebugCounterInfoOutput(true);
302 if (isRegistered) {
303 return listCounters(param1, output);
304 } else {
305 choice = Option.ERROR_BAD_MODULE_NAME;
306 }
307 } else if (param2.equals("all")) {
308 // get all counter info for a single module
309 boolean isRegistered = debugCounter.containsModuleName(param1);
310 if (isRegistered) {
311 choice = Option.ONE_MODULE;
312 } else {
313 choice = Option.ERROR_BAD_MODULE_NAME;
314 }
315 } else {
316 counterHierarchy += param2;
317 if (param3 != null) {
318 counterHierarchy += "/" + param3;
319 if (param4 != null) {
320 counterHierarchy += "/" + param4;
321 }
322 }
323 boolean isRegistered = debugCounter.
324 containsModuleCounterHierarchy(param1, counterHierarchy);
325 if (isRegistered) {
326 choice = Option.MODULE_COUNTER_HIERARCHY;
327 } else {
328 choice = Option.ERROR_BAD_MODULE_COUNTER_NAME;
329 }
330 }
331
332 switch (choice) {
333 case ONE_MODULE:
334 populateCounters(debugCounter.getModuleCounterValues(param1), output);
335 break;
336 case MODULE_COUNTER_HIERARCHY:
337 populateCounters(debugCounter.getCounterHierarchy(param1, counterHierarchy),
338 output);
339 break;
340 case ERROR_BAD_MODULE_NAME:
341 output.error = "Module name is not registered for debug-counters";
342 break;
343 case ERROR_BAD_MODULE_COUNTER_NAME:
344 output.error = "Counter not registered";
345 break;
346 case ERROR_BAD_PARAM:
347 default:
348 output.error = "Bad param";
349 }
350
351 return output;
352 }
353
354 private DebugCounterInfoOutput listCounters(String moduleName,
355 DebugCounterInfoOutput output) {
356 output.names = debugCounter.getModuleCounterList(moduleName);
357 return output;
358 }
359
360 private DebugCounterInfoOutput listCounters(DebugCounterInfoOutput output) {
361 output.names = debugCounter.getModuleList();
362 return output;
363 }
364
365 private void populateSingleCounter(DebugCounterInfo debugCounterInfo,
366 DebugCounterInfoOutput output) {
367 if (debugCounterInfo != null)
368 output.counterMap.put(debugCounterInfo.getCounterInfo().
369 getModuleCounterHierarchy(),
370 output.new DCInfo(debugCounterInfo));
371 }
372
373 private void populateCounters(List<DebugCounterInfo> counterValues,
374 DebugCounterInfoOutput output) {
375 for (DebugCounterInfo dci : counterValues) {
376 populateSingleCounter(dci, output);
377 }
378 }
379
380
381
382}