d372b87ab2b9e0ff023b3e07abd1d3f18feaae7a
[collectd.git] / src / ovs_events.c
1 /**
2  * collectd - src/ovs_events.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 of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * Authors:
25  *   Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
26  **/
27
28 #include "collectd.h"
29
30 #include "common.h" /* auxiliary functions */
31
32 #include "utils_ovs.h" /* OVS helpers */
33
34 #define OVS_EVENTS_IFACE_NAME_SIZE 128
35 #define OVS_EVENTS_IFACE_UUID_SIZE 64
36 #define OVS_EVENTS_EXT_IFACE_ID_SIZE 64
37 #define OVS_EVENTS_EXT_VM_UUID_SIZE 64
38 #define OVS_EVENTS_PLUGIN "ovs_events"
39 #define OVS_EVENTS_CTX_LOCK                                                    \
40   for (int __i = ovs_events_ctx_lock(); __i != 0; __i = ovs_events_ctx_unlock())
41
42 /* Link status type */
43 enum ovs_events_link_status_e { DOWN, UP };
44 typedef enum ovs_events_link_status_e ovs_events_link_status_t;
45
46 /* Interface info */
47 struct ovs_events_iface_info_s {
48   char name[OVS_EVENTS_IFACE_NAME_SIZE];           /* interface name */
49   char uuid[OVS_EVENTS_IFACE_UUID_SIZE];           /* interface UUID */
50   char ext_iface_id[OVS_EVENTS_EXT_IFACE_ID_SIZE]; /* external interface id */
51   char ext_vm_uuid[OVS_EVENTS_EXT_VM_UUID_SIZE];   /* external VM UUID */
52   ovs_events_link_status_t link_status;            /* interface link status */
53   struct ovs_events_iface_info_s *next;            /* next interface info */
54 };
55 typedef struct ovs_events_iface_info_s ovs_events_iface_info_t;
56
57 /* Interface list */
58 struct ovs_events_iface_list_s {
59   char name[OVS_EVENTS_IFACE_NAME_SIZE]; /* interface name */
60   struct ovs_events_iface_list_s *next;  /* next interface info */
61 };
62 typedef struct ovs_events_iface_list_s ovs_events_iface_list_t;
63
64 /* OVS events configuration data */
65 struct ovs_events_config_s {
66   _Bool send_notification;                 /* sent notification to collectd? */
67   char ovs_db_node[OVS_DB_ADDR_NODE_SIZE]; /* OVS DB node */
68   char ovs_db_serv[OVS_DB_ADDR_SERVICE_SIZE]; /* OVS DB service */
69   char ovs_db_unix[OVS_DB_ADDR_UNIX_SIZE];    /* OVS DB unix socket path */
70   ovs_events_iface_list_t *ifaces;            /* interface info */
71 };
72 typedef struct ovs_events_config_s ovs_events_config_t;
73
74 /* OVS events context type */
75 struct ovs_events_ctx_s {
76   pthread_mutex_t mutex;      /* mutex to lock the context */
77   ovs_db_t *ovs_db;           /* pointer to OVS DB instance */
78   ovs_events_config_t config; /* plugin config */
79   char *ovs_db_select_params; /* OVS DB select parameter request */
80   _Bool is_db_available;      /* specify whether OVS DB is available */
81 };
82 typedef struct ovs_events_ctx_s ovs_events_ctx_t;
83
84 /*
85  * Private variables
86  */
87 static ovs_events_ctx_t ovs_events_ctx = {
88     .mutex = PTHREAD_MUTEX_INITIALIZER,
89     .config = {.send_notification = 1,     /* send notification by default */
90                .ovs_db_node = "localhost", /* use default OVS DB node */
91                .ovs_db_serv = "6640"}      /* use default OVS DB service */
92 };
93
94 /* Forward declaration */
95 static int ovs_events_plugin_read(user_data_t *u);
96
97 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
98  * It always returns 1 when context is locked.
99  */
100 static int ovs_events_ctx_lock() {
101   pthread_mutex_lock(&ovs_events_ctx.mutex);
102   return (1);
103 }
104
105 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
106  * It always returns 0 when context is unlocked.
107  */
108 static int ovs_events_ctx_unlock() {
109   pthread_mutex_unlock(&ovs_events_ctx.mutex);
110   return (0);
111 }
112
113 /* Check if given interface name exists in configuration file. It
114  * returns 1 if exists otherwise 0. If no interfaces are configured,
115  * -1 is returned
116  */
117 static int ovs_events_config_iface_exists(const char *ifname) {
118   if (ovs_events_ctx.config.ifaces == NULL)
119     return (-1);
120
121   /* check if given interface exists */
122   for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
123        iface = iface->next)
124     if (strcmp(ifname, iface->name) == 0)
125       return (1);
126
127   return (0);
128 }
129
130 /* Get OVS DB select parameter request based on rfc7047,
131  * "Transact" & "Select" section
132  */
133 static char *ovs_events_get_select_params() {
134   size_t buff_size = 0;
135   size_t buff_off = 0;
136   char *opt_buff = NULL;
137   static const char params_fmt[] = "[\"Open_vSwitch\"%s]";
138   static const char option_fmt[] =
139       ",{\"op\":\"select\",\"table\":\"Interface\","
140       "\"where\":[[\"name\",\"==\",\"%s\"]],"
141       "\"columns\":[\"link_state\",\"external_ids\","
142       "\"name\",\"_uuid\"]}";
143   static const char default_opt[] =
144       ",{\"op\":\"select\",\"table\":\"Interface\","
145       "\"where\":[],\"columns\":[\"link_state\","
146       "\"external_ids\",\"name\",\"_uuid\"]}";
147   /* setup OVS DB interface condition */
148   for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
149        iface = iface->next) {
150     /* allocate new buffer (format size + ifname len is good enough) */
151     buff_size += sizeof(option_fmt) + strlen(iface->name);
152     char *new_buff = realloc(opt_buff, buff_size);
153     if (new_buff == NULL) {
154       sfree(opt_buff);
155       return NULL;
156     }
157     opt_buff = new_buff;
158     int ret = ssnprintf(opt_buff + buff_off, buff_size - buff_off, option_fmt,
159                         iface->name);
160     if (ret < 0) {
161       sfree(opt_buff);
162       return NULL;
163     }
164     buff_off += ret;
165   }
166   /* if no interfaces are configured, use default params */
167   if (opt_buff == NULL)
168     if ((opt_buff = strdup(default_opt)) == NULL)
169       return NULL;
170
171   /* allocate memory for OVS DB select params */
172   size_t params_size = sizeof(params_fmt) + strlen(opt_buff);
173   char *params_buff = calloc(1, params_size);
174   if (params_buff == NULL) {
175     sfree(opt_buff);
176     return NULL;
177   }
178
179   /* create OVS DB select params */
180   if (ssnprintf(params_buff, params_size, params_fmt, opt_buff) < 0)
181     sfree(params_buff);
182
183   sfree(opt_buff);
184   return params_buff;
185 }
186
187 /* Release memory allocated for configuration data */
188 static void ovs_events_config_free() {
189   ovs_events_iface_list_t *del_iface = NULL;
190   sfree(ovs_events_ctx.ovs_db_select_params);
191   while (ovs_events_ctx.config.ifaces) {
192     del_iface = ovs_events_ctx.config.ifaces;
193     ovs_events_ctx.config.ifaces = ovs_events_ctx.config.ifaces->next;
194     sfree(del_iface);
195   }
196 }
197
198 /* Parse/process "Interfaces" configuration option. Returns 0 if success
199  * otherwise -1 (error)
200  */
201 static int ovs_events_config_get_interfaces(const oconfig_item_t *ci) {
202   for (int j = 0; j < ci->values_num; j++) {
203     /* check interface name type */
204     if (ci->values[j].type != OCONFIG_TYPE_STRING) {
205       ERROR(OVS_EVENTS_PLUGIN
206             ": given interface name is not a string [idx=%d]", j);
207       return (-1);
208     }
209     /* allocate memory for configured interface */
210     ovs_events_iface_list_t *new_iface = calloc(1, sizeof(*new_iface));
211     if (new_iface == NULL) {
212       ERROR(OVS_EVENTS_PLUGIN ": calloc () copy interface name fail");
213       return (-1);
214     } else {
215       /* store interface name */
216       sstrncpy(new_iface->name, ci->values[j].value.string,
217                sizeof(new_iface->name));
218       new_iface->next = ovs_events_ctx.config.ifaces;
219       ovs_events_ctx.config.ifaces = new_iface;
220       DEBUG(OVS_EVENTS_PLUGIN ": found monitored interface \"%s\"",
221             new_iface->name);
222     }
223   }
224   return (0);
225 }
226
227 /* Parse plugin configuration file and store the config
228  * in allocated memory. Returns negative value in case of error.
229  */
230 static int ovs_events_plugin_config(oconfig_item_t *ci) {
231   _Bool dispatch_values = 0;
232   for (int i = 0; i < ci->children_num; i++) {
233     oconfig_item_t *child = ci->children + i;
234     if (strcasecmp("SendNotification", child->key) == 0) {
235       if (cf_util_get_boolean(child,
236                               &ovs_events_ctx.config.send_notification) != 0) {
237         ovs_events_config_free();
238         return (-1);
239       }
240     } else if (strcasecmp("Address", child->key) == 0) {
241       if (cf_util_get_string_buffer(
242               child, ovs_events_ctx.config.ovs_db_node,
243               sizeof(ovs_events_ctx.config.ovs_db_node)) != 0) {
244         ovs_events_config_free();
245         return (-1);
246       }
247     } else if (strcasecmp("Port", child->key) == 0) {
248       char *service = NULL;
249       if (cf_util_get_service(child, &service) != 0) {
250         ovs_events_config_free();
251         return (-1);
252       }
253       strncpy(ovs_events_ctx.config.ovs_db_serv, service,
254               sizeof(ovs_events_ctx.config.ovs_db_serv));
255       sfree(service);
256     } else if (strcasecmp("Socket", child->key) == 0) {
257       if (cf_util_get_string_buffer(
258               child, ovs_events_ctx.config.ovs_db_unix,
259               sizeof(ovs_events_ctx.config.ovs_db_unix)) != 0) {
260         ovs_events_config_free();
261         return (-1);
262       }
263     } else if (strcasecmp("Interfaces", child->key) == 0) {
264       if (ovs_events_config_get_interfaces(child) != 0) {
265         ovs_events_config_free();
266         return (-1);
267       }
268     } else if (strcasecmp("DispatchValues", child->key) == 0) {
269       if (cf_util_get_boolean(child, &dispatch_values) != 0) {
270         ovs_events_config_free();
271         return (-1);
272       }
273     } else {
274       ERROR(OVS_EVENTS_PLUGIN ": option '%s' is not allowed here", child->key);
275       ovs_events_config_free();
276       return (-1);
277     }
278   }
279   /* Check and warn about invalid configuration */
280   if (!ovs_events_ctx.config.send_notification && !dispatch_values) {
281       WARNING(OVS_EVENTS_PLUGIN ": send notification and dispatch values "
282               "options are disabled. No information will be dispatched by the "
283               "plugin. Please check your configuration");
284   }
285   /* Dispatch link status values if configured */
286   if (dispatch_values)
287     return plugin_register_complex_read(NULL, OVS_EVENTS_PLUGIN,
288                                         ovs_events_plugin_read, 0, NULL);
289
290   return (0);
291 }
292
293 /* Dispatch OVS interface link status event to collectd */
294 static void ovs_events_dispatch_notification(const ovs_events_iface_info_t *ifinfo) {
295   const char *msg_link_status = NULL;
296   notification_t n = {
297       NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
298
299   /* convert link status to message string */
300   switch (ifinfo->link_status) {
301   case UP:
302     msg_link_status = "UP";
303     n.severity = NOTIF_OKAY;
304     break;
305   case DOWN:
306     msg_link_status = "DOWN";
307     n.severity = NOTIF_WARNING;
308     break;
309   default:
310     ERROR(OVS_EVENTS_PLUGIN ": unknown interface link status");
311     return;
312   }
313
314   /* add interface metadata to the notification */
315   if (plugin_notification_meta_add_string(&n, "uuid", ifinfo->uuid) < 0) {
316     ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
317     return;
318   }
319
320   if (strlen(ifinfo->ext_vm_uuid) > 0) {
321     if (plugin_notification_meta_add_string(&n, "vm-uuid",
322                                             ifinfo->ext_vm_uuid) < 0) {
323       ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
324       return;
325     }
326   }
327
328   if (strlen(ifinfo->ext_iface_id) > 0) {
329     if (plugin_notification_meta_add_string(&n, "iface-id",
330                                             ifinfo->ext_iface_id) < 0) {
331       ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
332       return;
333     }
334   }
335
336   /* fill the notification data */
337   ssnprintf(n.message, sizeof(n.message),
338             "link state of \"%s\" interface has been changed to \"%s\"",
339             ifinfo->name, msg_link_status);
340   sstrncpy(n.host, hostname_g, sizeof(n.host));
341   sstrncpy(n.plugin_instance, ifinfo->name, sizeof(n.plugin_instance));
342   sstrncpy(n.type, "gauge", sizeof(n.type));
343   sstrncpy(n.type_instance, "link_status", sizeof(n.type_instance));
344   plugin_dispatch_notification(&n);
345 }
346
347 /* Dispatch OVS interface link status value to collectd */
348 static void ovs_events_link_status_submit(const ovs_events_iface_info_t *ifinfo) {
349   value_list_t vl = VALUE_LIST_INIT;
350   meta_data_t *meta = NULL;
351
352   /* add interface metadata to the submit value */
353   if ((meta = meta_data_create()) != NULL) {
354     if (meta_data_add_string(meta, "uuid", ifinfo->uuid) < 0)
355       ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
356
357     if (strlen(ifinfo->ext_vm_uuid) > 0)
358       if (meta_data_add_string(meta, "vm-uuid", ifinfo->ext_vm_uuid) < 0)
359         ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
360
361     if (strlen(ifinfo->ext_iface_id) > 0)
362       if (meta_data_add_string(meta, "iface-id", ifinfo->ext_iface_id) < 0)
363         ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
364     vl.meta = meta;
365   } else
366     ERROR(OVS_EVENTS_PLUGIN ": create metadata failed");
367
368   vl.time = cdtime();
369   vl.values = &(value_t){.gauge = (gauge_t)ifinfo->link_status};
370   vl.values_len = 1;
371   sstrncpy(vl.plugin, OVS_EVENTS_PLUGIN, sizeof(vl.plugin));
372   sstrncpy(vl.plugin_instance, ifinfo->name, sizeof(vl.plugin_instance));
373   sstrncpy(vl.type, "gauge", sizeof(vl.type));
374   sstrncpy(vl.type_instance, "link_status", sizeof(vl.type_instance));
375   plugin_dispatch_values(&vl);
376   meta_data_destroy(meta);
377 }
378
379 /* Dispatch OVS DB terminate connection event to collectd */
380 static void ovs_events_dispatch_terminate_notification(const char *msg) {
381   notification_t n = {
382       NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
383   sstrncpy(n.message, msg, sizeof(n.message));
384   sstrncpy(n.host, hostname_g, sizeof(n.host));
385   plugin_dispatch_notification(&n);
386 }
387
388 /* Get OVS DB interface information and stores it into
389  * ovs_events_iface_info_t structure */
390 static int ovs_events_get_iface_info(yajl_val jobject,
391                                      ovs_events_iface_info_t *ifinfo) {
392   yajl_val jexternal_ids = NULL;
393   yajl_val jvalue = NULL;
394   yajl_val juuid = NULL;
395   const char *state = NULL;
396
397   /* check YAJL type */
398   if (!YAJL_IS_OBJECT(jobject))
399     return (-1);
400
401   /* zero the interface info structure */
402   memset(ifinfo, 0, sizeof(*ifinfo));
403
404   /* try to find external_ids, name and link_state fields */
405   jexternal_ids = ovs_utils_get_value_by_key(jobject, "external_ids");
406   if (jexternal_ids == NULL || ifinfo == NULL)
407     return (-1);
408
409   /* get iface-id from external_ids field */
410   jvalue = ovs_utils_get_map_value(jexternal_ids, "iface-id");
411   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
412     sstrncpy(ifinfo->ext_iface_id, YAJL_GET_STRING(jvalue),
413              sizeof(ifinfo->ext_iface_id));
414
415   /* get vm-uuid from external_ids field */
416   jvalue = ovs_utils_get_map_value(jexternal_ids, "vm-uuid");
417   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
418     sstrncpy(ifinfo->ext_vm_uuid, YAJL_GET_STRING(jvalue),
419              sizeof(ifinfo->ext_vm_uuid));
420
421   /* get interface uuid */
422   jvalue = ovs_utils_get_value_by_key(jobject, "_uuid");
423   if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue) ||
424       YAJL_GET_ARRAY(jvalue)->len != 2)
425     return (-1);
426   juuid = YAJL_GET_ARRAY(jvalue)->values[1];
427   if (juuid == NULL || !YAJL_IS_STRING(juuid))
428     return (-1);
429   sstrncpy(ifinfo->uuid, YAJL_GET_STRING(juuid), sizeof(ifinfo->uuid));
430
431   /* get interface name */
432   jvalue = ovs_utils_get_value_by_key(jobject, "name");
433   if (jvalue == NULL || !YAJL_IS_STRING(jvalue))
434     return (-1);
435   sstrncpy(ifinfo->name, YAJL_GET_STRING(jvalue), sizeof(ifinfo->name));
436
437   /* get OVS DB interface link status */
438   jvalue = ovs_utils_get_value_by_key(jobject, "link_state");
439   if (jvalue != NULL && ((state = YAJL_GET_STRING(jvalue)) != NULL)) {
440     /* convert OVS table link state to link status */
441     if (strcmp(state, "up") == 0)
442       ifinfo->link_status = UP;
443     else if (strcmp(state, "down") == 0)
444       ifinfo->link_status = DOWN;
445   }
446   return (0);
447 }
448
449 /* Process OVS DB update table event. It handles link status update event(s)
450  * and dispatches the value(s) to collectd if interface name matches one of
451  * interfaces specified in configuration file.
452  */
453 static void ovs_events_table_update_cb(yajl_val jupdates) {
454   yajl_val jnew_val = NULL;
455   yajl_val jupdate = NULL;
456   yajl_val jrow_update = NULL;
457   ovs_events_iface_info_t ifinfo;
458
459   /* JSON "Interface" table update example:
460    * ---------------------------------
461    * {"Interface":
462    *  {
463    *   "9adf1db2-29ca-4140-ab22-ae347a4484de":
464    *    {
465    *     "new":
466    *      {
467    *       "name":"br0",
468    *       "link_state":"up"
469    *      },
470    *     "old":
471    *      {
472    *       "link_state":"down"
473    *      }
474    *    }
475    *  }
476    * }
477    */
478   if (!YAJL_IS_OBJECT(jupdates) || !(YAJL_GET_OBJECT(jupdates)->len > 0)) {
479     ERROR(OVS_EVENTS_PLUGIN ": unexpected OVS DB update event received");
480     return;
481   }
482   /* verify if this is a table event */
483   jupdate = YAJL_GET_OBJECT(jupdates)->values[0];
484   if (!YAJL_IS_OBJECT(jupdate)) {
485     ERROR(OVS_EVENTS_PLUGIN ": unexpected table update event received");
486     return;
487   }
488   /* go through all row updates  */
489   for (size_t row_index = 0; row_index < YAJL_GET_OBJECT(jupdate)->len;
490        ++row_index) {
491     jrow_update = YAJL_GET_OBJECT(jupdate)->values[row_index];
492
493     /* check row update */
494     jnew_val = ovs_utils_get_value_by_key(jrow_update, "new");
495     if (jnew_val == NULL) {
496       ERROR(OVS_EVENTS_PLUGIN ": unexpected row update received");
497       return;
498     }
499     /* get OVS DB interface information */
500     if (ovs_events_get_iface_info(jnew_val, &ifinfo) < 0) {
501       ERROR(OVS_EVENTS_PLUGIN
502             " :unexpected interface information data received");
503       return;
504     }
505     if (ovs_events_config_iface_exists(ifinfo.name) != 0) {
506       DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
507             ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
508       /* dispatch notification */
509       ovs_events_dispatch_notification(&ifinfo);
510     }
511   }
512 }
513
514 /* OVS DB reply callback. It parses reply, receives
515  * interface information and dispatches the info to
516  * collectd
517  */
518 static void ovs_events_poll_result_cb(yajl_val jresult, yajl_val jerror) {
519   yajl_val *jvalues = NULL;
520   yajl_val jvalue = NULL;
521   ovs_events_iface_info_t ifinfo;
522
523   if (!YAJL_IS_NULL(jerror)) {
524     ERROR(OVS_EVENTS_PLUGIN "error received by OVS DB server");
525     return;
526   }
527
528   /* result should be an array */
529   if (!YAJL_IS_ARRAY(jresult)) {
530     ERROR(OVS_EVENTS_PLUGIN "invalid data (array is expected)");
531     return;
532   }
533
534   /* go through all rows and get interface info */
535   jvalues = YAJL_GET_ARRAY(jresult)->values;
536   for (size_t i = 0; i < YAJL_GET_ARRAY(jresult)->len; i++) {
537     jvalue = ovs_utils_get_value_by_key(jvalues[i], "rows");
538     if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue)) {
539       ERROR(OVS_EVENTS_PLUGIN "invalid data (array of rows is expected)");
540       return;
541     }
542     /* get interfaces info */
543     for (size_t j = 0; j < YAJL_GET_ARRAY(jvalue)->len; j++) {
544       if (ovs_events_get_iface_info(YAJL_GET_ARRAY(jvalue)->values[j],
545                                     &ifinfo) < 0) {
546         ERROR(OVS_EVENTS_PLUGIN
547               "unexpected interface information data received");
548         return;
549       }
550       DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
551             ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
552       ovs_events_link_status_submit(&ifinfo);
553     }
554   }
555 }
556
557 /* Setup OVS DB table callback. It subscribes to OVS DB 'Interface' table
558  * to receive link status event(s).
559  */
560 static void ovs_events_conn_initialize(ovs_db_t *pdb) {
561   const char tb_name[] = "Interface";
562   const char *columns[] = {"_uuid", "external_ids", "name", "link_state", NULL};
563
564   /* register update link status event if needed */
565   if (ovs_events_ctx.config.send_notification) {
566     int ret = ovs_db_table_cb_register(pdb, tb_name, columns,
567                                        ovs_events_table_update_cb, NULL,
568                                        OVS_DB_TABLE_CB_FLAG_MODIFY);
569     if (ret < 0) {
570       ERROR(OVS_EVENTS_PLUGIN ": register OVS DB update callback failed");
571       return;
572     }
573   }
574   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 1; }
575   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB connection has been initialized");
576 }
577
578 /* OVS DB terminate connection notification callback */
579 static void ovs_events_conn_terminate() {
580   const char msg[] = "OVS DB connection has been lost";
581   if (ovs_events_ctx.config.send_notification)
582     ovs_events_dispatch_terminate_notification(msg);
583   WARNING(OVS_EVENTS_PLUGIN ": %s", msg);
584   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 0; }
585 }
586
587 /* Read OVS DB interface link status callback */
588 static int ovs_events_plugin_read(__attribute__((unused)) user_data_t *u) {
589   _Bool is_connected = 0;
590   OVS_EVENTS_CTX_LOCK { is_connected = ovs_events_ctx.is_db_available; }
591   if (is_connected)
592     if (ovs_db_send_request(ovs_events_ctx.ovs_db, "transact",
593                             ovs_events_ctx.ovs_db_select_params,
594                             ovs_events_poll_result_cb) < 0) {
595       ERROR(OVS_EVENTS_PLUGIN ": get interface info failed");
596       return (-1);
597     }
598   return (0);
599 }
600
601 /* Initialize OVS plugin */
602 static int ovs_events_plugin_init(void) {
603   ovs_db_t *ovs_db = NULL;
604   ovs_db_callback_t cb = {.post_conn_init = ovs_events_conn_initialize,
605                           .post_conn_terminate = ovs_events_conn_terminate};
606
607   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB address=%s, service=%s, unix=%s",
608         ovs_events_ctx.config.ovs_db_node, ovs_events_ctx.config.ovs_db_serv,
609         ovs_events_ctx.config.ovs_db_unix);
610
611   /* generate OVS DB select condition based on list on configured interfaces */
612   ovs_events_ctx.ovs_db_select_params = ovs_events_get_select_params();
613   if (ovs_events_ctx.ovs_db_select_params == NULL) {
614     ERROR(OVS_EVENTS_PLUGIN ": fail to get OVS DB select condition");
615     goto ovs_events_failure;
616   }
617
618   /* initialize OVS DB */
619   ovs_db = ovs_db_init(ovs_events_ctx.config.ovs_db_node,
620                        ovs_events_ctx.config.ovs_db_serv,
621                        ovs_events_ctx.config.ovs_db_unix, &cb);
622   if (ovs_db == NULL) {
623     ERROR(OVS_EVENTS_PLUGIN ": fail to connect to OVS DB server");
624     goto ovs_events_failure;
625   }
626
627   /* store OVS DB handler */
628   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.ovs_db = ovs_db; }
629
630   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been initialized");
631   return (0);
632
633 ovs_events_failure:
634   ERROR(OVS_EVENTS_PLUGIN ": plugin initialize failed");
635   /* release allocated memory */
636   ovs_events_config_free();
637   return (-1);
638 }
639
640 /* Shutdown OVS plugin */
641 static int ovs_events_plugin_shutdown(void) {
642   /* destroy OVS DB */
643   if (ovs_db_destroy(ovs_events_ctx.ovs_db))
644     ERROR(OVS_EVENTS_PLUGIN ": OVSDB object destroy failed");
645
646   /* release memory allocated for config */
647   ovs_events_config_free();
648
649   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been destroyed");
650   return (0);
651 }
652
653 /* Register OVS plugin callbacks */
654 void module_register(void) {
655   plugin_register_complex_config(OVS_EVENTS_PLUGIN, ovs_events_plugin_config);
656   plugin_register_init(OVS_EVENTS_PLUGIN, ovs_events_plugin_init);
657   plugin_register_shutdown(OVS_EVENTS_PLUGIN, ovs_events_plugin_shutdown);
658 }