Use sstrncpy instead of strncpy in sysevent plugin
[collectd.git] / src / ovs_stats.c
1 /*
2  * collectd - src/ovs_stats.c
3  *
4  * Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11  * of the Software, and to permit persons to whom the Software is furnished to
12  * do
13  * so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all
17  * copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  *
27  * Authors:
28  *   Taras Chornyi <tarasx.chornyi@intel.com>
29  */
30
31 #include "common.h"
32
33 #include "utils_ovs.h" /* OvS helpers */
34
35 /* Plugin name */
36 static const char plugin_name[] = "ovs_stats";
37
38 typedef enum iface_counter {
39   not_supported = -1,
40   collisions,
41   rx_bytes,
42   rx_crc_err,
43   rx_dropped,
44   rx_errors,
45   rx_frame_err,
46   rx_over_err,
47   rx_packets,
48   tx_bytes,
49   tx_dropped,
50   tx_errors,
51   tx_packets,
52   rx_1_to_64_packets,
53   rx_65_to_127_packets,
54   rx_128_to_255_packets,
55   rx_256_to_511_packets,
56   rx_512_to_1023_packets,
57   rx_1024_to_1522_packets,
58   rx_1523_to_max_packets,
59   tx_1_to_64_packets,
60   tx_65_to_127_packets,
61   tx_128_to_255_packets,
62   tx_256_to_511_packets,
63   tx_512_to_1023_packets,
64   tx_1024_to_1522_packets,
65   tx_1523_to_max_packets,
66   tx_multicast_packets,
67   rx_broadcast_packets,
68   tx_broadcast_packets,
69   rx_undersized_errors,
70   rx_oversize_errors,
71   rx_fragmented_errors,
72   rx_jabber_errors,
73   __iface_counter_max
74 } iface_counter;
75
76 #define IFACE_COUNTER_MAX (__iface_counter_max - 1)
77 #define IFACE_COUNTER_COUNT (__iface_counter_max)
78 #define PORT_NAME_SIZE_MAX 255
79 #define UUID_SIZE 64
80
81 typedef struct port_s {
82   char name[PORT_NAME_SIZE_MAX];      /* Port name */
83   char port_uuid[UUID_SIZE];          /* Port table _uuid */
84   char iface_uuid[UUID_SIZE];         /* Interface table uuid */
85   char ex_iface_id[UUID_SIZE];        /* External iface id */
86   char ex_vm_id[UUID_SIZE];           /* External vm id */
87   int64_t stats[IFACE_COUNTER_COUNT]; /* Port statistics */
88   struct bridge_list_s *br;           /* Pointer to bridge */
89   struct port_s *next;                /* Next port */
90 } port_list_t;
91
92 typedef struct bridge_list_s {
93   char *name;                 /* Bridge name */
94   struct bridge_list_s *next; /* Next bridge*/
95 } bridge_list_t;
96
97 #define cnt_str(x) [x] = #x
98
99 static const char *const iface_counter_table[IFACE_COUNTER_COUNT] = {
100     cnt_str(collisions),
101     cnt_str(rx_bytes),
102     cnt_str(rx_crc_err),
103     cnt_str(rx_dropped),
104     cnt_str(rx_errors),
105     cnt_str(rx_frame_err),
106     cnt_str(rx_over_err),
107     cnt_str(rx_packets),
108     cnt_str(tx_bytes),
109     cnt_str(tx_dropped),
110     cnt_str(tx_errors),
111     cnt_str(tx_packets),
112     cnt_str(rx_1_to_64_packets),
113     cnt_str(rx_65_to_127_packets),
114     cnt_str(rx_128_to_255_packets),
115     cnt_str(rx_256_to_511_packets),
116     cnt_str(rx_512_to_1023_packets),
117     cnt_str(rx_1024_to_1522_packets),
118     cnt_str(rx_1523_to_max_packets),
119     cnt_str(tx_1_to_64_packets),
120     cnt_str(tx_65_to_127_packets),
121     cnt_str(tx_128_to_255_packets),
122     cnt_str(tx_256_to_511_packets),
123     cnt_str(tx_512_to_1023_packets),
124     cnt_str(tx_1024_to_1522_packets),
125     cnt_str(tx_1523_to_max_packets),
126     cnt_str(tx_multicast_packets),
127     cnt_str(rx_broadcast_packets),
128     cnt_str(tx_broadcast_packets),
129     cnt_str(rx_undersized_errors),
130     cnt_str(rx_oversize_errors),
131     cnt_str(rx_fragmented_errors),
132     cnt_str(rx_jabber_errors),
133 };
134
135 #undef cnt_str
136
137 /* Entry into the list of network bridges */
138 static bridge_list_t *g_bridge_list_head;
139
140 /* Entry into the list of monitored network bridges */
141 static bridge_list_t *g_monitored_bridge_list_head;
142
143 /* entry into the list of network bridges */
144 static port_list_t *g_port_list_head;
145
146 /* lock for statistics cache */
147 static pthread_mutex_t g_stats_lock;
148
149 /* OvS DB socket */
150 static ovs_db_t *g_ovs_db;
151
152 /* OVS stats configuration data */
153 struct ovs_stats_config_s {
154   char ovs_db_node[OVS_DB_ADDR_NODE_SIZE];    /* OVS DB node */
155   char ovs_db_serv[OVS_DB_ADDR_SERVICE_SIZE]; /* OVS DB service */
156   char ovs_db_unix[OVS_DB_ADDR_UNIX_SIZE];    /* OVS DB unix socket path */
157 };
158 typedef struct ovs_stats_config_s ovs_stats_config_t;
159
160 static ovs_stats_config_t ovs_stats_cfg = {
161     .ovs_db_node = "localhost", /* use default OVS DB node */
162     .ovs_db_serv = "6640",      /* use default OVS DB service */
163 };
164
165 static iface_counter ovs_stats_counter_name_to_type(const char *counter) {
166   iface_counter index = not_supported;
167
168   if (counter == NULL)
169     return not_supported;
170
171   for (int i = 0; i < IFACE_COUNTER_COUNT; i++) {
172     if (strncmp(iface_counter_table[i], counter,
173                 strlen(iface_counter_table[i])) == 0) {
174       index = i;
175       break;
176     }
177   }
178   return index;
179 }
180
181 static void ovs_stats_submit_one(const char *dev, const char *type,
182                                  const char *type_instance, derive_t value,
183                                  meta_data_t *meta) {
184   /* if counter is less than 0 - skip it*/
185   if (value < 0)
186     return;
187   value_list_t vl = VALUE_LIST_INIT;
188
189   vl.values = &(value_t){.derive = value};
190   vl.values_len = 1;
191   vl.meta = meta;
192
193   sstrncpy(vl.plugin, plugin_name, sizeof(vl.plugin));
194   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
195   sstrncpy(vl.type, type, sizeof(vl.type));
196
197   if (type_instance != NULL)
198     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
199
200   plugin_dispatch_values(&vl);
201 }
202
203 static void ovs_stats_submit_two(const char *dev, const char *type,
204                                  const char *type_instance, derive_t rx,
205                                  derive_t tx, meta_data_t *meta) {
206   /* if counter is less than 0 - skip it*/
207   if (rx < 0 || tx < 0)
208     return;
209   value_list_t vl = VALUE_LIST_INIT;
210   value_t values[] = {{.derive = rx}, {.derive = tx}};
211
212   vl.values = values;
213   vl.values_len = STATIC_ARRAY_SIZE(values);
214   vl.meta = meta;
215
216   sstrncpy(vl.plugin, plugin_name, sizeof(vl.plugin));
217   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
218   sstrncpy(vl.type, type, sizeof(vl.type));
219
220   if (type_instance != NULL)
221     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
222
223   plugin_dispatch_values(&vl);
224 }
225
226 static port_list_t *ovs_stats_get_port(const char *uuid) {
227   if (uuid == NULL)
228     return NULL;
229
230   for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) {
231     if (strncmp(port->port_uuid, uuid, strlen(port->port_uuid)) == 0)
232       return port;
233   }
234   return NULL;
235 }
236
237 static port_list_t *ovs_stats_get_port_by_name(const char *name) {
238   if (name == NULL)
239     return NULL;
240
241   for (port_list_t *port = g_port_list_head; port != NULL; port = port->next)
242     if ((strncmp(port->name, name, strlen(port->name)) == 0) &&
243         strlen(name) == strlen(port->name))
244       return port;
245   return NULL;
246 }
247
248 /* Create or get port by port uuid */
249 static port_list_t *ovs_stats_new_port(bridge_list_t *bridge,
250                                        const char *uuid) {
251   if (uuid == NULL)
252     return NULL;
253
254   port_list_t *port = ovs_stats_get_port(uuid);
255
256   if (port == NULL) {
257     port = (port_list_t *)calloc(1, sizeof(port_list_t));
258     if (!port) {
259       ERROR("%s: Error allocating port", plugin_name);
260       return NULL;
261     }
262     memset(port->stats, -1, sizeof(int64_t[IFACE_COUNTER_COUNT]));
263     sstrncpy(port->port_uuid, uuid, sizeof(port->port_uuid));
264     pthread_mutex_lock(&g_stats_lock);
265     port->next = g_port_list_head;
266     g_port_list_head = port;
267     pthread_mutex_unlock(&g_stats_lock);
268   }
269   if (bridge != NULL) {
270     pthread_mutex_lock(&g_stats_lock);
271     port->br = bridge;
272     pthread_mutex_unlock(&g_stats_lock);
273   }
274   return port;
275 }
276
277 /* Get bridge by name*/
278 static bridge_list_t *ovs_stats_get_bridge(bridge_list_t *head,
279                                            const char *name) {
280   if (name == NULL)
281     return NULL;
282
283   for (bridge_list_t *bridge = head; bridge != NULL; bridge = bridge->next) {
284     if ((strncmp(bridge->name, name, strlen(bridge->name)) == 0) &&
285         strlen(name) == strlen(bridge->name))
286       return bridge;
287   }
288   return NULL;
289 }
290
291 /* Delete bridge */
292 static int ovs_stats_del_bridge(yajl_val bridge) {
293   const char *old[] = {"old", NULL};
294   const char *name[] = {"name", NULL};
295
296   yajl_val row;
297
298   if (bridge && YAJL_IS_OBJECT(bridge)) {
299     row = yajl_tree_get(bridge, old, yajl_t_object);
300     if (row && YAJL_IS_OBJECT(row)) {
301       yajl_val br_name = yajl_tree_get(row, name, yajl_t_string);
302       if (br_name && YAJL_IS_STRING(br_name)) {
303         bridge_list_t *prev_br = g_bridge_list_head;
304         for (bridge_list_t *br = g_bridge_list_head; br != NULL;
305              prev_br = br, br = br->next) {
306           if ((strncmp(br->name, br_name->u.string, strlen(br->name)) == 0) &&
307               strlen(br->name) == strlen(br_name->u.string)) {
308             if (br == g_bridge_list_head)
309               g_bridge_list_head = br->next;
310             else
311               prev_br->next = br->next;
312             sfree(br->name);
313             sfree(br);
314             break;
315           }
316         }
317       }
318     }
319   } else
320     WARNING("%s: Incorrect data for deleting bridge", plugin_name);
321   return 0;
322 }
323
324 /* Update Bridge. Create bridge ports*/
325 static int ovs_stats_update_bridge(yajl_val bridge) {
326   const char *new[] = {"new", NULL};
327   const char *name[] = {"name", NULL};
328   const char *ports[] = {"ports", NULL};
329   bridge_list_t *br = NULL;
330
331   if (bridge && YAJL_IS_OBJECT(bridge)) {
332     yajl_val row = yajl_tree_get(bridge, new, yajl_t_object);
333     if (row && YAJL_IS_OBJECT(row)) {
334       yajl_val br_name = yajl_tree_get(row, name, yajl_t_string);
335       yajl_val br_ports = yajl_tree_get(row, ports, yajl_t_array);
336       if (br_name && YAJL_IS_STRING(br_name)) {
337         br = ovs_stats_get_bridge(g_bridge_list_head, YAJL_GET_STRING(br_name));
338         pthread_mutex_lock(&g_stats_lock);
339         if (br == NULL) {
340           br = calloc(1, sizeof(*br));
341           if (!br) {
342             pthread_mutex_unlock(&g_stats_lock);
343             ERROR("%s: calloc(%zu) failed.", plugin_name, sizeof(*br));
344             return -1;
345           }
346           char *tmp = YAJL_GET_STRING(br_name);
347
348           if (tmp != NULL)
349             br->name = strdup(tmp);
350           if (br->name == NULL) {
351             sfree(br);
352             pthread_mutex_unlock(&g_stats_lock);
353             ERROR("%s: strdup failed.", plugin_name);
354             return -1;
355           }
356           br->next = g_bridge_list_head;
357           g_bridge_list_head = br;
358         }
359         pthread_mutex_unlock(&g_stats_lock);
360       }
361       if (br_ports && YAJL_IS_ARRAY(br_ports)) {
362         char *tmp = YAJL_GET_STRING(br_ports->u.array.values[0]);
363         if (tmp != NULL && strcmp("set", tmp) == 0) {
364           yajl_val *array = YAJL_GET_ARRAY(br_ports)->values;
365           size_t array_len = YAJL_GET_ARRAY(br_ports)->len;
366           if (array != NULL && array_len > 0 && YAJL_IS_ARRAY(array[1])) {
367             if (YAJL_GET_ARRAY(array[1]) == NULL)
368               goto failure;
369             else {
370               yajl_val *ports_arr = YAJL_GET_ARRAY(array[1])->values;
371               size_t ports_num = YAJL_GET_ARRAY(array[1])->len;
372               for (size_t i = 0; i < ports_num && ports_arr != NULL; i++) {
373                 tmp = YAJL_GET_STRING(ports_arr[i]->u.array.values[1]);
374                 if (tmp != NULL)
375                   ovs_stats_new_port(br, tmp);
376                 else
377                   goto failure;
378               }
379             }
380           }
381         } else
382           ovs_stats_new_port(br, YAJL_GET_STRING(br_ports->u.array.values[1]));
383       }
384     }
385   } else {
386     goto failure;
387   }
388
389   return 0;
390
391 failure:
392   ERROR("Incorrect JSON Bridge data");
393   return -1;
394 }
395
396 /* Handle JSON with Bridge Table change event */
397 static void ovs_stats_bridge_table_change_cb(yajl_val jupdates) {
398   /* Bridge Table update example JSON data
399     {
400       "Bridge": {
401         "bb1f8965-5775-46d9-b820-236ca8edbedc": {
402           "new": {
403             "name": "br0",
404             "ports": [
405               "set",
406               [
407                 [
408                   "uuid",
409                   "117f1a07-7ef0-458a-865c-ec7fbb85bc01"
410                 ],
411                 [
412                   "uuid",
413                   "12fd8bdc-e950-4281-aaa9-46e185658f79"
414                 ]
415               ]
416             ]
417           }
418         }
419       }
420     }
421    */
422   const char *path[] = {"Bridge", NULL};
423
424   yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object);
425
426   if (bridges && YAJL_IS_OBJECT(bridges)) {
427     for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
428       yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i];
429       ovs_stats_update_bridge(bridge);
430     }
431   }
432 }
433
434 /* Handle Bridge Table delete event */
435 static void ovs_stats_bridge_table_delete_cb(yajl_val jupdates) {
436   const char *path[] = {"Bridge", NULL};
437   yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object);
438   yajl_val bridge;
439   if (bridges && YAJL_IS_OBJECT(bridges)) {
440     pthread_mutex_lock(&g_stats_lock);
441     for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
442       bridge = YAJL_GET_OBJECT(bridges)->values[i];
443       ovs_stats_del_bridge(bridge);
444     }
445     pthread_mutex_unlock(&g_stats_lock);
446   }
447   return;
448 }
449
450 /* Handle JSON with Bridge table initial values */
451 static void ovs_stats_bridge_table_result_cb(yajl_val jresult,
452                                              yajl_val jerror) {
453   if (YAJL_IS_NULL(jerror))
454     ovs_stats_bridge_table_change_cb(jresult);
455   else
456     ERROR("%s: Error received from OvSDB. Table: Bridge", plugin_name);
457   return;
458 }
459
460 /* Update port name */
461 static int ovs_stats_update_port(const char *uuid, yajl_val port) {
462   const char *new[] = {"new", NULL};
463   const char *name[] = {"name", NULL};
464   yajl_val row;
465   port_list_t *portentry = NULL;
466   if (port && YAJL_IS_OBJECT(port)) {
467     row = yajl_tree_get(port, new, yajl_t_object);
468     if (row && YAJL_IS_OBJECT(row)) {
469       yajl_val port_name = yajl_tree_get(row, name, yajl_t_string);
470       if (port_name && YAJL_IS_STRING(port_name)) {
471         portentry = ovs_stats_get_port(uuid);
472         if (portentry == NULL)
473           portentry = ovs_stats_new_port(NULL, uuid);
474         if (portentry) {
475           pthread_mutex_lock(&g_stats_lock);
476           sstrncpy(portentry->name, YAJL_GET_STRING(port_name),
477                    sizeof(portentry->name));
478           pthread_mutex_unlock(&g_stats_lock);
479         }
480       }
481     }
482   } else {
483     ERROR("Incorrect JSON Port data");
484     return -1;
485   }
486   return 0;
487 }
488
489 /* Delete port from global port list */
490 static int ovs_stats_del_port(const char *uuid) {
491   port_list_t *prev_port = g_port_list_head;
492   for (port_list_t *port = g_port_list_head; port != NULL;
493        prev_port = port, port = port->next) {
494     if (strncmp(port->port_uuid, uuid, strlen(port->port_uuid)) == 0) {
495       if (port == g_port_list_head)
496         g_port_list_head = port->next;
497       else
498         prev_port->next = port->next;
499       sfree(port);
500       break;
501     }
502   }
503   return 0;
504 }
505
506 /* Handle JSON with Port Table change event */
507 static void ovs_stats_port_table_change_cb(yajl_val jupdates) {
508   /* Port Table update example JSON data
509     {
510       "Port": {
511         "ab107d6f-28a1-4257-b1cc-5b742821db8a": {
512           "new": {
513             "name": "br1",
514             "interfaces": [
515               "uuid",
516               "33a289a0-1d34-4e46-a3c2-3e4066fbecc6"
517             ]
518           }
519         }
520       }
521     }
522    */
523   const char *path[] = {"Port", NULL};
524   yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
525   yajl_val port;
526   if (ports && YAJL_IS_OBJECT(ports)) {
527     for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
528       port = YAJL_GET_OBJECT(ports)->values[i];
529       ovs_stats_update_port(YAJL_GET_OBJECT(ports)->keys[i], port);
530     }
531   }
532   return;
533 }
534
535 /* Handle JSON with Port table initial values */
536 static void ovs_stats_port_table_result_cb(yajl_val jresult, yajl_val jerror) {
537   if (YAJL_IS_NULL(jerror))
538     ovs_stats_port_table_change_cb(jresult);
539   else
540     ERROR("%s: Error received from OvSDB. Table: Port", plugin_name);
541   return;
542 }
543
544 /* Handle Port Table delete event */
545 static void ovs_stats_port_table_delete_cb(yajl_val jupdates) {
546   const char *path[] = {"Port", NULL};
547   yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
548   pthread_mutex_lock(&g_stats_lock);
549   if (ports && YAJL_IS_OBJECT(ports))
550     for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
551       ovs_stats_del_port(YAJL_GET_OBJECT(ports)->keys[i]);
552     }
553   pthread_mutex_unlock(&g_stats_lock);
554   return;
555 }
556
557 /* Update interface statistics */
558 static int ovs_stats_update_iface_stats(port_list_t *port, yajl_val stats) {
559   yajl_val stat;
560   iface_counter counter_index = 0;
561   char *counter_name = NULL;
562   int64_t counter_value = 0;
563   if (stats && YAJL_IS_ARRAY(stats))
564     for (size_t i = 0; i < YAJL_GET_ARRAY(stats)->len; i++) {
565       stat = YAJL_GET_ARRAY(stats)->values[i];
566       if (!YAJL_IS_ARRAY(stat))
567         return -1;
568       counter_name = YAJL_GET_STRING(YAJL_GET_ARRAY(stat)->values[0]);
569       counter_index = ovs_stats_counter_name_to_type(counter_name);
570       counter_value = YAJL_GET_INTEGER(YAJL_GET_ARRAY(stat)->values[1]);
571       if (counter_index == not_supported)
572         continue;
573       port->stats[counter_index] = counter_value;
574     }
575
576   return 0;
577 }
578
579 /* Update interface external_ids */
580 static int ovs_stats_update_iface_ext_ids(port_list_t *port, yajl_val ext_ids) {
581   yajl_val ext_id;
582   char *key;
583   char *value;
584
585   if (ext_ids && YAJL_IS_ARRAY(ext_ids))
586     for (size_t i = 0; i < YAJL_GET_ARRAY(ext_ids)->len; i++) {
587       ext_id = YAJL_GET_ARRAY(ext_ids)->values[i];
588       if (!YAJL_IS_ARRAY(ext_id))
589         return -1;
590       key = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[0]);
591       value = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[1]);
592       if (key && value) {
593         if (strncmp(key, "iface-id", strlen(key)) == 0)
594           sstrncpy(port->ex_iface_id, value, sizeof(port->ex_iface_id));
595         else if (strncmp(key, "vm-uuid", strlen(key)) == 0)
596           sstrncpy(port->ex_vm_id, value, sizeof(port->ex_vm_id));
597       }
598     }
599
600   return 0;
601 }
602
603 /* Get interface statistic and external_ids */
604 static int ovs_stats_update_iface(yajl_val iface) {
605   if (!iface || !YAJL_IS_OBJECT(iface)) {
606     ERROR("ovs_stats plugin: incorrect JSON port data");
607     return -1;
608   }
609
610   yajl_val row = ovs_utils_get_value_by_key(iface, "new");
611   if (!row || !YAJL_IS_OBJECT(row))
612     return 0;
613
614   yajl_val iface_name = ovs_utils_get_value_by_key(row, "name");
615   if (!iface_name || !YAJL_IS_STRING(iface_name))
616     return 0;
617
618   port_list_t *port = ovs_stats_get_port_by_name(YAJL_GET_STRING(iface_name));
619   if (port == NULL)
620     return 0;
621
622   yajl_val iface_stats = ovs_utils_get_value_by_key(row, "statistics");
623   yajl_val iface_ext_ids = ovs_utils_get_value_by_key(row, "external_ids");
624   yajl_val iface_uuid = ovs_utils_get_value_by_key(row, "_uuid");
625   /*
626    * {
627         "statistics": [
628           "map",
629           [
630             [
631               "collisions",
632               0
633             ],
634             . . .
635             [
636               "tx_packets",
637               0
638             ]
639           ]
640         ]
641       }
642    Check that statistics is an array with 2 elements
643    */
644   if (iface_stats && YAJL_IS_ARRAY(iface_stats) &&
645       YAJL_GET_ARRAY(iface_stats)->len == 2)
646     ovs_stats_update_iface_stats(port, YAJL_GET_ARRAY(iface_stats)->values[1]);
647   if (iface_ext_ids && YAJL_IS_ARRAY(iface_ext_ids))
648     ovs_stats_update_iface_ext_ids(port,
649                                    YAJL_GET_ARRAY(iface_ext_ids)->values[1]);
650   if (iface_uuid && YAJL_IS_ARRAY(iface_uuid) &&
651       YAJL_GET_ARRAY(iface_uuid)->len == 2 &&
652       YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]) != NULL)
653     sstrncpy(port->iface_uuid,
654              YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]),
655              sizeof(port->iface_uuid));
656   else {
657     ERROR("ovs_stats plugin: incorrect JSON interface data");
658     return -1;
659   }
660
661   return 0;
662 }
663
664 /* Handle JSON with Interface Table change event */
665 static void ovs_stats_interface_table_change_cb(yajl_val jupdates) {
666   /* Interface Table update example JSON data
667     {
668       "Interface": {
669         "33a289a0-1d34-4e46-a3c2-3e4066fbecc6": {
670           "new": {
671             "name": "br1",
672             "statistics": [
673               "map",
674               [
675                 [
676                   "collisions",
677                   0
678                 ],
679                 [
680                   "rx_bytes",
681                   0
682                 ],
683                . . .
684                 [
685                   "tx_packets",
686                   12617
687                 ]
688               ]
689             ],
690             "_uuid": [
691               "uuid",
692               "33a289a0-1d34-4e46-a3c2-3e4066fbecc6"
693             ]
694             "external_ids": [
695                 "map",
696                 [
697                   [
698                     "attached-mac",
699                     "fa:16:3e:7c:1c:4b"
700                   ],
701                   [
702                     "iface-id",
703                     "a61b7e2b-6951-488a-b4c6-6e91343960b2"
704                   ],
705                   [
706                     "iface-status",
707                     "active"
708                   ]
709                 ]
710               ]
711           }
712         }
713       }
714     }
715    */
716   const char *path[] = {"Interface", NULL};
717   yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
718   pthread_mutex_lock(&g_stats_lock);
719   if (ports && YAJL_IS_OBJECT(ports))
720     for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++)
721       ovs_stats_update_iface(YAJL_GET_OBJECT(ports)->values[i]);
722   pthread_mutex_unlock(&g_stats_lock);
723   return;
724 }
725
726 /* Handle JSON with Interface table initial values */
727 static void ovs_stats_interface_table_result_cb(yajl_val jresult,
728                                                 yajl_val jerror) {
729   if (YAJL_IS_NULL(jerror))
730     ovs_stats_interface_table_change_cb(jresult);
731   else
732     ERROR("%s: Error received from OvSDB. Table: Interface", plugin_name);
733   return;
734 }
735
736 /* Setup OVS DB table callbacks  */
737 static void ovs_stats_initialize(ovs_db_t *pdb) {
738   const char *bridge_columns[] = {"name", "ports", NULL};
739   const char *port_columns[] = {"name", "interfaces", NULL};
740   const char *interface_columns[] = {"name", "statistics", "_uuid",
741                                      "external_ids", NULL};
742
743   /* subscribe to a tables */
744   ovs_db_table_cb_register(
745       pdb, "Bridge", bridge_columns, ovs_stats_bridge_table_change_cb,
746       ovs_stats_bridge_table_result_cb,
747       OVS_DB_TABLE_CB_FLAG_INITIAL | OVS_DB_TABLE_CB_FLAG_INSERT |
748           OVS_DB_TABLE_CB_FLAG_MODIFY);
749
750   ovs_db_table_cb_register(pdb, "Bridge", bridge_columns,
751                            ovs_stats_bridge_table_delete_cb, NULL,
752                            OVS_DB_TABLE_CB_FLAG_DELETE);
753
754   ovs_db_table_cb_register(
755       pdb, "Port", port_columns, ovs_stats_port_table_change_cb,
756       ovs_stats_port_table_result_cb,
757       OVS_DB_TABLE_CB_FLAG_INITIAL | OVS_DB_TABLE_CB_FLAG_INSERT |
758           OVS_DB_TABLE_CB_FLAG_MODIFY);
759
760   ovs_db_table_cb_register(pdb, "Port", port_columns,
761                            ovs_stats_port_table_delete_cb, NULL,
762                            OVS_DB_TABLE_CB_FLAG_DELETE);
763
764   ovs_db_table_cb_register(
765       pdb, "Interface", interface_columns, ovs_stats_interface_table_change_cb,
766       ovs_stats_interface_table_result_cb,
767       OVS_DB_TABLE_CB_FLAG_INITIAL | OVS_DB_TABLE_CB_FLAG_INSERT |
768           OVS_DB_TABLE_CB_FLAG_MODIFY);
769 }
770
771 /* Check if bridge is configured to be monitored in config file */
772 static int ovs_stats_is_monitored_bridge(const char *br_name) {
773   /* if no bridges are configured, return true */
774   if (g_monitored_bridge_list_head == NULL)
775     return 1;
776
777   /* check if given bridge exists */
778   if (ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name) != NULL)
779     return 1;
780
781   return 0;
782 }
783
784 /* Delete all ports from port list */
785 static void ovs_stats_free_port_list(port_list_t *head) {
786   for (port_list_t *i = head; i != NULL;) {
787     port_list_t *del = i;
788     i = i->next;
789     sfree(del);
790   }
791 }
792
793 /* Delete all bridges from bridge list */
794 static void ovs_stats_free_bridge_list(bridge_list_t *head) {
795   for (bridge_list_t *i = head; i != NULL;) {
796     bridge_list_t *del = i;
797     i = i->next;
798     sfree(del->name);
799     sfree(del);
800   }
801 }
802
803 /* Handle OVSDB lost connection callback */
804 static void ovs_stats_conn_terminate() {
805   WARNING("Lost connection to OVSDB server");
806   pthread_mutex_lock(&g_stats_lock);
807   ovs_stats_free_bridge_list(g_bridge_list_head);
808   g_bridge_list_head = NULL;
809   ovs_stats_free_port_list(g_port_list_head);
810   g_port_list_head = NULL;
811   pthread_mutex_unlock(&g_stats_lock);
812 }
813
814 /* Parse plugin configuration file and store the config
815  * in allocated memory. Returns negative value in case of error.
816  */
817 static int ovs_stats_plugin_config(oconfig_item_t *ci) {
818   bridge_list_t *bridge;
819
820   for (int i = 0; i < ci->children_num; i++) {
821     oconfig_item_t *child = ci->children + i;
822     if (strcasecmp("Address", child->key) == 0) {
823       if (cf_util_get_string_buffer(child, ovs_stats_cfg.ovs_db_node,
824                                     OVS_DB_ADDR_NODE_SIZE) != 0) {
825         ERROR("%s: parse '%s' option failed", plugin_name, child->key);
826         return -1;
827       }
828     } else if (strcasecmp("Port", child->key) == 0) {
829       if (cf_util_get_string_buffer(child, ovs_stats_cfg.ovs_db_serv,
830                                     OVS_DB_ADDR_SERVICE_SIZE) != 0) {
831         ERROR("%s: parse '%s' option failed", plugin_name, child->key);
832         return -1;
833       }
834     } else if (strcasecmp("Socket", child->key) == 0) {
835       if (cf_util_get_string_buffer(child, ovs_stats_cfg.ovs_db_unix,
836                                     OVS_DB_ADDR_UNIX_SIZE) != 0) {
837         ERROR("%s: parse '%s' option failed", plugin_name, child->key);
838         return -1;
839       }
840     } else if (strcasecmp("Bridges", child->key) == 0) {
841       for (int j = 0; j < child->values_num; j++) {
842         /* check value type */
843         if (child->values[j].type != OCONFIG_TYPE_STRING) {
844           ERROR("%s: Wrong bridge name [idx=%d]. "
845                 "Bridge name should be string",
846                 plugin_name, j);
847           goto cleanup_fail;
848         }
849         /* get value */
850         char const *br_name = child->values[j].value.string;
851         if ((bridge = ovs_stats_get_bridge(g_monitored_bridge_list_head,
852                                            br_name)) == NULL) {
853           if ((bridge = calloc(1, sizeof(bridge_list_t))) == NULL) {
854             ERROR("%s: Error allocating memory for bridge", plugin_name);
855             goto cleanup_fail;
856           } else {
857             char *br_name_dup = strdup(br_name);
858             if (br_name_dup == NULL) {
859               ERROR("%s: strdup() copy bridge name fail", plugin_name);
860               sfree(bridge);
861               goto cleanup_fail;
862             }
863
864             pthread_mutex_lock(&g_stats_lock);
865             /* store bridge name */
866             bridge->name = br_name_dup;
867             bridge->next = g_monitored_bridge_list_head;
868             g_monitored_bridge_list_head = bridge;
869             pthread_mutex_unlock(&g_stats_lock);
870             DEBUG("%s: found monitored interface \"%s\"", plugin_name, br_name);
871           }
872         }
873       }
874     } else {
875       WARNING("%s: option '%s' not allowed here", plugin_name, child->key);
876       goto cleanup_fail;
877     }
878   }
879   return 0;
880
881 cleanup_fail:
882   ovs_stats_free_bridge_list(g_monitored_bridge_list_head);
883   return -1;
884 }
885
886 /* Initialize OvS Stats plugin*/
887 static int ovs_stats_plugin_init(void) {
888   ovs_db_callback_t cb = {.post_conn_init = ovs_stats_initialize,
889                           .post_conn_terminate = ovs_stats_conn_terminate};
890
891   INFO("%s: Connecting to OVS DB using address=%s, service=%s, unix=%s",
892        plugin_name, ovs_stats_cfg.ovs_db_node, ovs_stats_cfg.ovs_db_serv,
893        ovs_stats_cfg.ovs_db_unix);
894   /* connect to OvS DB */
895   if ((g_ovs_db =
896            ovs_db_init(ovs_stats_cfg.ovs_db_node, ovs_stats_cfg.ovs_db_serv,
897                        ovs_stats_cfg.ovs_db_unix, &cb)) == NULL) {
898     ERROR("%s: plugin: failed to connect to OvS DB server", plugin_name);
899     return -1;
900   }
901   int err = pthread_mutex_init(&g_stats_lock, NULL);
902   if (err < 0) {
903     ERROR("%s: plugin: failed to initialize cache lock", plugin_name);
904     ovs_db_destroy(g_ovs_db);
905     return -1;
906   }
907   return 0;
908 }
909
910 /* OvS stats read callback. Read bridge/port information and submit it*/
911 static int ovs_stats_plugin_read(__attribute__((unused)) user_data_t *ud) {
912   bridge_list_t *bridge;
913   port_list_t *port;
914   char devname[PORT_NAME_SIZE_MAX * 2];
915
916   pthread_mutex_lock(&g_stats_lock);
917   for (bridge = g_bridge_list_head; bridge != NULL; bridge = bridge->next) {
918     if (ovs_stats_is_monitored_bridge(bridge->name)) {
919       for (port = g_port_list_head; port != NULL; port = port->next)
920         if (port->br == bridge) {
921           if (strlen(port->name) == 0)
922             /* Skip port w/o name. This is possible when read callback
923              * is called after Interface Table update callback but before
924              * Port table Update callback. Will add this port on next read */
925             continue;
926           meta_data_t *meta = meta_data_create();
927           if (meta != NULL) {
928             meta_data_add_string(meta, "uuid", port->iface_uuid);
929             if (strlen(port->ex_vm_id))
930               meta_data_add_string(meta, "vm-uuid", port->ex_vm_id);
931             if (strlen(port->ex_iface_id))
932               meta_data_add_string(meta, "iface-id", port->ex_iface_id);
933           }
934           snprintf(devname, sizeof(devname), "%s.%s", bridge->name, port->name);
935           ovs_stats_submit_one(devname, "if_collisions", NULL,
936                                port->stats[collisions], meta);
937           ovs_stats_submit_two(devname, "if_dropped", NULL,
938                                port->stats[rx_dropped], port->stats[tx_dropped],
939                                meta);
940           ovs_stats_submit_two(devname, "if_errors", NULL,
941                                port->stats[rx_errors], port->stats[tx_errors],
942                                meta);
943           ovs_stats_submit_two(devname, "if_packets", NULL,
944                                port->stats[rx_packets], port->stats[tx_packets],
945                                meta);
946           ovs_stats_submit_one(devname, "if_rx_errors", "crc",
947                                port->stats[rx_crc_err], meta);
948           ovs_stats_submit_one(devname, "if_rx_errors", "frame",
949                                port->stats[rx_frame_err], meta);
950           ovs_stats_submit_one(devname, "if_rx_errors", "over",
951                                port->stats[rx_over_err], meta);
952           ovs_stats_submit_one(devname, "if_rx_octets", NULL,
953                                port->stats[rx_bytes], meta);
954           ovs_stats_submit_one(devname, "if_tx_octets", NULL,
955                                port->stats[tx_bytes], meta);
956           ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets",
957                                port->stats[rx_1_to_64_packets],
958                                port->stats[tx_1_to_64_packets], meta);
959           ovs_stats_submit_two(devname, "if_packets", "65_to_127_packets",
960                                port->stats[rx_65_to_127_packets],
961                                port->stats[tx_65_to_127_packets], meta);
962           ovs_stats_submit_two(devname, "if_packets", "128_to_255_packets",
963                                port->stats[rx_128_to_255_packets],
964                                port->stats[tx_128_to_255_packets], meta);
965           ovs_stats_submit_two(devname, "if_packets", "256_to_511_packets",
966                                port->stats[rx_256_to_511_packets],
967                                port->stats[tx_256_to_511_packets], meta);
968           ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets",
969                                port->stats[rx_512_to_1023_packets],
970                                port->stats[tx_512_to_1023_packets], meta);
971           ovs_stats_submit_two(devname, "if_packets", "1024_to_1522_packets",
972                                port->stats[rx_1024_to_1522_packets],
973                                port->stats[tx_1024_to_1522_packets], meta);
974           ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets",
975                                port->stats[rx_1523_to_max_packets],
976                                port->stats[tx_1523_to_max_packets], meta);
977           ovs_stats_submit_two(devname, "if_packets", "broadcast_packets",
978                                port->stats[rx_broadcast_packets],
979                                port->stats[tx_broadcast_packets], meta);
980           ovs_stats_submit_one(devname, "if_multicast", "tx_multicast_packets",
981                                port->stats[tx_multicast_packets], meta);
982           ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersized_errors",
983                                port->stats[rx_undersized_errors], meta);
984           ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors",
985                                port->stats[rx_oversize_errors], meta);
986           ovs_stats_submit_one(devname, "if_rx_errors", "rx_fragmented_errors",
987                                port->stats[rx_fragmented_errors], meta);
988           ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors",
989                                port->stats[rx_jabber_errors], meta);
990
991           meta_data_destroy(meta);
992         }
993     } else
994       continue;
995   }
996   pthread_mutex_unlock(&g_stats_lock);
997   return 0;
998 }
999
1000 /* Shutdown OvS Stats plugin */
1001 static int ovs_stats_plugin_shutdown(void) {
1002   DEBUG("OvS Statistics plugin shutting down");
1003   ovs_db_destroy(g_ovs_db);
1004   pthread_mutex_lock(&g_stats_lock);
1005   ovs_stats_free_bridge_list(g_bridge_list_head);
1006   ovs_stats_free_bridge_list(g_monitored_bridge_list_head);
1007   ovs_stats_free_port_list(g_port_list_head);
1008   pthread_mutex_unlock(&g_stats_lock);
1009   pthread_mutex_destroy(&g_stats_lock);
1010   return 0;
1011 }
1012
1013 /* Register OvS Stats plugin callbacks */
1014 void module_register(void) {
1015   plugin_register_complex_config(plugin_name, ovs_stats_plugin_config);
1016   plugin_register_init(plugin_name, ovs_stats_plugin_init);
1017   plugin_register_complex_read(NULL, plugin_name, ovs_stats_plugin_read, 0,
1018                                NULL);
1019   plugin_register_shutdown(plugin_name, ovs_stats_plugin_shutdown);
1020 }