mactime: add a "top" command to watch device stats
[vpp.git] / src / plugins / mactime / mactime_test.c
1 /*
2  * mactime.c - skeleton vpp-api-test plug-in
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain 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,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <vat/vat.h>
18 #include <vlibapi/api.h>
19 #include <vlibmemory/api.h>
20 #include <vppinfra/error.h>
21 #include <vppinfra/time_range.h>
22 #include <vnet/ethernet/ethernet.h>
23 #include <mactime/mactime_device.h>
24 #include <vpp-api/client/stat_client.h>
25
26 /* Declare message IDs */
27 #include <mactime/mactime.api_enum.h>
28 #include <mactime/mactime.api_types.h>
29
30 typedef struct
31 {
32   /* device table */
33   mactime_device_t *devices;
34   uword *device_by_device_name;
35   u32 vpp_table_epoch;
36
37   /* time range setup */
38   f64 sunday_midnight;
39   clib_timebase_t timebase;
40   f64 timezone_offset;
41
42   /* API message ID base */
43   u16 msg_id_base;
44   vat_main_t *vat_main;
45 } mactime_test_main_t;
46
47 mactime_test_main_t mactime_test_main;
48
49 #define __plugin_msg_base mactime_test_main.msg_id_base
50 #include <vlibapi/vat_helper_macros.h>
51
52 static int
53 api_mactime_enable_disable (vat_main_t * vam)
54 {
55   unformat_input_t *i = vam->input;
56   int enable_disable = 1;
57   u32 sw_if_index = ~0;
58   vl_api_mactime_enable_disable_t *mp;
59   int ret;
60
61   /* Parse args required to build the message */
62   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
63     {
64       if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
65         ;
66       else if (unformat (i, "sw_if_index %d", &sw_if_index))
67         ;
68       else if (unformat (i, "disable"))
69         enable_disable = 0;
70       else
71         break;
72     }
73
74   if (sw_if_index == ~0)
75     {
76       errmsg ("missing interface name / explicit sw_if_index number \n");
77       return -99;
78     }
79
80   /* Construct the API message */
81   M (MACTIME_ENABLE_DISABLE, mp);
82   mp->sw_if_index = ntohl (sw_if_index);
83   mp->enable_disable = enable_disable;
84
85   /* send it... */
86   S (mp);
87
88   /* Wait for a reply... */
89   W (ret);
90   return ret;
91 }
92
93 #if VPP_API_TEST_BUILTIN
94 extern u8 *format_bytes_with_width (u8 * s, va_list * va);
95 #else
96 u8 *
97 format_bytes_with_width (u8 * s, va_list * va)
98 {
99   uword nbytes = va_arg (*va, u64);
100   int width = va_arg (*va, int);
101   f64 nbytes_f64;
102   u8 *fmt;
103   char *suffix = "";
104
105   if (width > 0)
106     fmt = format (0, "%%%d.3f%%s%c", width, 0);
107   else
108     fmt = format (0, "%%.3f%%s%c", 0);
109
110   if (nbytes > (1024ULL * 1024ULL * 1024ULL))
111     {
112       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
113       suffix = "G";
114     }
115   else if (nbytes > (1024ULL * 1024ULL))
116     {
117       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
118       suffix = "M";
119     }
120   else if (nbytes > 1024ULL)
121     {
122       nbytes_f64 = ((f64) nbytes) / (1024.0);
123       suffix = "K";
124     }
125   else
126     {
127       nbytes_f64 = (f64) nbytes;
128       suffix = "B";
129     }
130
131   s = format (s, (char *) fmt, nbytes_f64, suffix);
132   vec_free (fmt);
133   return s;
134 }
135 #endif
136
137 static u8 *
138 format_device (u8 * s, va_list * args)
139 {
140   mactime_device_t *dp = va_arg (*args, mactime_device_t *);
141   mactime_test_main_t *mm = &mactime_test_main;
142   int verbose = va_arg (*args, int);
143   int current_status = 99;
144   char *status_string;
145   u8 *macstring = 0;
146   f64 now;
147   int j;
148
149   if (dp == 0)
150     {
151       s = format (s, "%-15s %5s %18s %14s %10s %11s %13s",
152                   "Device Name", "Index", "Addresses", "Status",
153                   "AllowPkt", "AllowByte", "DropPkt");
154       vec_add1 (s, '\n');
155       return s;
156     }
157
158   now = clib_timebase_now (&mm->timebase);
159
160   /* Check dynamic ranges */
161   for (j = 0; j < vec_len (dp->ranges); j++)
162     {
163       clib_timebase_range_t *r = dp->ranges + j;
164       f64 start0, end0;
165
166       start0 = r->start + mm->sunday_midnight;
167       end0 = r->end + mm->sunday_midnight;
168       if (verbose)
169         s = format (s, "  Range %d: %U - %U\n", j,
170                     format_clib_timebase_time, start0,
171                     format_clib_timebase_time, end0);
172
173       if (now >= start0 && now <= end0)
174         {
175           if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
176             current_status = 3;
177           else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
178             current_status = 5;
179           else
180             current_status = 2;
181           if (verbose)
182             {
183               s = format (s, "  Time in range %d:", j);
184               s = format (s, "     %U - %U\n",
185                           format_clib_timebase_time, start0,
186                           format_clib_timebase_time, end0);
187             }
188           goto print;
189         }
190     }
191   if (verbose && j)
192     s = format (s, "  No range match.\n");
193   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
194     current_status = 0;
195   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
196     current_status = 1;
197   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
198     current_status = 2;
199   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
200     current_status = 3;
201   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
202     current_status = 4;
203
204 print:
205   macstring = format (0, "%U", format_mac_address, dp->mac_address);
206   switch (current_status)
207     {
208     case 0:
209       status_string = "static drop";
210       break;
211     case 1:
212       status_string = "static allow";
213       break;
214     case 2:
215       status_string = "dynamic drop";
216       break;
217     case 3:
218       status_string = "dynamic allow";
219       break;
220     case 4:
221       status_string = "d-quota inact";
222       break;
223     case 5:
224       status_string = "d-quota activ";
225       break;
226     default:
227       status_string = "code bug!";
228       break;
229     }
230
231   s = format (s, "%-15s %5d %18s %14s\n",
232               dp->device_name, dp->pool_index, macstring, status_string);
233   vec_free (macstring);
234
235   if (dp->data_quota > 0)
236     {
237       s = format (s, "%-59s %s%U %s%U", " ", "Quota ",
238                   format_bytes_with_width, dp->data_quota, 10,
239                   "Use ", format_bytes_with_width, dp->data_used_in_range, 8);
240       vec_add1 (s, '\n');
241     }
242   return s;
243 }
244
245 static int
246 api_mactime_dump (vat_main_t * vam)
247 {
248   mactime_test_main_t *tm = &mactime_test_main;
249   unformat_input_t *i = vam->input;
250   vl_api_mactime_dump_t *mp;
251   int verbose = 0;
252   int ret;
253   f64 now;
254   mactime_device_t *dev;
255
256   now = clib_timebase_now (&tm->timebase);
257
258   if (PREDICT_FALSE ((now - tm->sunday_midnight) > 86400.0 * 7.0))
259     tm->sunday_midnight = clib_timebase_find_sunday_midnight (now);
260
261   /* Parse args required to build the message */
262   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
263     {
264       if (unformat (i, "force"))
265         tm->vpp_table_epoch = 0;
266       else if (unformat (i, "verbose"))
267         verbose = 1;
268       else
269         break;
270     }
271
272   /* Construct the API message */
273   M (MACTIME_DUMP, mp);
274   mp->my_table_epoch = clib_host_to_net_u32 (tm->vpp_table_epoch);
275
276   /* send it... */
277   S (mp);
278
279   /* Wait for a reply... */
280   W (ret);
281
282   fformat (vam->ofp, "%U", format_device, 0 /* header */ , 0 /* verbose */ );
283   /* *INDENT-OFF* */
284   pool_foreach (dev, tm->devices,
285   ({
286     fformat (vam->ofp, "%U", format_device, dev, verbose);
287   }));
288   /* *INDENT-ON* */
289
290   return ret;
291 }
292
293 /* These two ought to be in a library somewhere but they aren't */
294 static uword
295 my_unformat_mac_address (unformat_input_t * input, va_list * args)
296 {
297   u8 *a = va_arg (*args, u8 *);
298   return unformat (input, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3],
299                    &a[4], &a[5]);
300 }
301
302 static u8 *
303 my_format_mac_address (u8 * s, va_list * args)
304 {
305   u8 *a = va_arg (*args, u8 *);
306   return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
307                  a[0], a[1], a[2], a[3], a[4], a[5]);
308 }
309
310 static int
311 api_mactime_add_del_range (vat_main_t * vam)
312 {
313   unformat_input_t *i = vam->input;
314   vl_api_mactime_add_del_range_t *mp;
315   u8 mac_address[8];
316   u8 *device_name = 0;
317   clib_timebase_range_t *rp = 0;
318   int name_set = 0;
319   int mac_set = 0;
320   u8 is_add = 1;
321   u8 allow = 0;
322   u8 allow_quota = 0;
323   u8 drop = 0;
324   u8 no_udp_10001 = 0;
325   u64 data_quota = 0;
326   int ret;
327   int ii;
328
329   /* Parse args required to build the message */
330   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
331     {
332       if (unformat (i, "name %s", &device_name))
333         {
334           vec_add1 (device_name, 0);
335           name_set = 1;
336         }
337       else if (unformat (i, "allow-range %U",
338                          unformat_clib_timebase_range_vector, &rp))
339         allow = 1;
340       else if (unformat (i, "allow-quota-range %U",
341                          unformat_clib_timebase_range_vector, &rp))
342         allow_quota = 1;
343       else if (unformat (i, "drop-range %U",
344                          unformat_clib_timebase_range_vector, &rp))
345         drop = 1;
346       else if (unformat (i, "allow-static"))
347         allow = 1;
348       else if (unformat (i, "drop-static"))
349         drop = 1;
350       else if (unformat (i, "no-udp-10001"))
351         no_udp_10001 = 1;
352       else if (unformat (i, "mac %U", my_unformat_mac_address, mac_address))
353         mac_set = 1;
354       else if (unformat (i, "del"))
355         is_add = 0;
356       else if (unformat (i, "data-quota %lldM", &data_quota))
357         data_quota <<= 20;
358       else if (unformat (i, "data-quota %lldG", &data_quota))
359         data_quota <<= 30;
360       else
361         break;
362     }
363
364   /* Sanity checks */
365   if (mac_set == 0)
366     {
367       vec_free (rp);
368       vec_free (device_name);
369       errmsg ("mac address required, not set\n");
370       return -99;
371     }
372
373   /* allow-range / drop-range parse errors cause this condition */
374   if (is_add && allow == 0 && drop == 0 && allow_quota == 0)
375     {
376       vec_free (rp);
377       vec_free (device_name);
378       errmsg ("parse error...\n");
379       return -99;
380     }
381
382   /* Unlikely, but check anyhow */
383   if (vec_len (device_name) > ARRAY_LEN (mp->device_name))
384     {
385       vec_free (rp);
386       vec_free (device_name);
387       errmsg ("device name too long, max %d\n", ARRAY_LEN (mp->device_name));
388       return -99;
389     }
390
391   /* Cough up a device name if none set */
392   if (name_set == 0)
393     {
394       device_name = format (0, "mac %U%c", my_format_mac_address,
395                             mac_address, 0);
396     }
397
398   /* Construct the API message */
399   M2 (MACTIME_ADD_DEL_RANGE, mp, sizeof (rp[0]) * vec_len (rp));
400   mp->is_add = is_add;
401   mp->drop = drop;
402   mp->allow = allow;
403   mp->allow_quota = allow_quota;
404   mp->no_udp_10001 = no_udp_10001;
405   mp->data_quota = clib_host_to_net_u64 (data_quota);
406   memcpy (mp->mac_address, mac_address, sizeof (mp->mac_address));
407   memcpy (mp->device_name, device_name, vec_len (device_name));
408   mp->count = clib_host_to_net_u32 (vec_len (rp));
409
410   for (ii = 0; ii < vec_len (rp); ii++)
411     {
412       mp->ranges[ii].start = rp[ii].start;
413       mp->ranges[ii].end = rp[ii].end;
414     }
415
416   vec_free (rp);
417   vec_free (device_name);
418
419   /* send it... */
420   S (mp);
421
422   /* Wait for a reply... */
423   W (ret);
424   return ret;
425 }
426
427 /* We shouldn't get these... */
428 static void
429 vl_api_mactime_details_t_handler (vl_api_mactime_details_t * mp)
430 {
431   clib_warning ("WARNING: stub called...");
432 }
433
434
435 #include <mactime/mactime.api_test.c>
436
437 /*
438  * fd.io coding-style-patch-verification: ON
439  *
440  * Local Variables:
441  * eval: (c-set-style "gnu")
442  * End:
443  */