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